|
Building Web-Based Communities with Java 2 Enterprise Edition
by Bill Schneider (bschneid@arsdigita.com)
Submitted on: 2000-07-13
Last updated: 2001-05-10
ArsDigita : ArsDigita Systems Journal : One article
The Java 2 Enterprise Edition (J2EE, see http://java.sun.com/products/j2ee)
is a platform standard, established by Sun Microsystems, that brings the
"write once, run anywhere" Java architecture to server-side and distributed
applications. This contrasts with Java's first days, when the language made
headlines for its client-side uses.
Java became well-known initially because of its virtual machine
architecture, which makes it possible to build secure "sandboxes" for Java
applets running in Web browsers, and to build graphical applications that are
independent of the underlying processor or operating system on the user's
machine. The same virtual machine architecture means that server-side or
multi-tier applications written to the J2EE standard may run under any
application server, on any hardware, and with any operating system that
supports the J2EE API.
Because a significant portion of the J2EE platform is directed towards
making web-enabled applications and integrating with relational databases, we
can build a web-based community with J2EE. This article will give a brief
description of J2EE, describe the requirements for a part of a web-based
community application, and explain in depth the portions of J2EE that we could
use to build such an application.
J2EE: What is it?J2EE is a set of Java API standards for building Web
and enterprise applications. A J2EE application runs inside a container or
application server, which provides an implementation of the J2EE API
services.
The following APIs are all components of J2EE; many of them, such as the
servlet API and Java Database Connectivity (JDBC), were previously available
and supported before the complete J2EE standard:
- Servlets: Java's answer to CGI scripts, servlets are a standard API for
writing server-side Web components.
- Java Server Pages (JSP): a shorthand for writing servlets, where you can
mix Java code and literal output
- Enterprise Java Beans (EJB): an API for implementing and managing Java
components in a distributed system.
- Java Transactions: an API for managing distributed transactions
- Java Database Connectivity (JDBC): an API for integrating Java with an
RDBMS
- Java Naming and Directory Interface (JNDI)
- Java Messaging Service (JMS): an API for asynchronous messaging
- JavaMail: a set of APIs for handling Internet e-mail
- Java Naming and Directory Interface (JNDI): an API for directory lookup
services in J2EE systems; also integrates with LDAP.
J2EE: What it isn't
J2EE is not an application serverWhile the J2EE consists of several
powerful APIs, it is not an application server itself; rather, it is a
standard that any application server vendor may implement, either in whole, or
in part. Many application server vendors offer different levels of J2EE
support within their own product line.
Sun makes a reference implementation (see http://java.sun.com/j2ee/download)
of a J2EE server available for download. Some other J2EE server products
include:
JDBC is not a database driverAlthough the JDBC interface specifies
the classes and methods that Java applications use to access a database,
developers need to obtain the specific database driver for their RDBMS product
and operating system. Many JDBC drivers are written partially in native code,
for performance. There are, however, some pure-Java or "Type 4" JDBC drivers.
Type 4 drivers may only be applicable for TCP/IP socket connections to the
database; using IPC may require native calls, depending on the underlying
operating system.
J2EE is not an application architectureMany J2EE application servers
expect to run three-tiered (client, server, RDBMS) applications, but not every
J2EE application need be three-tiered. On the contrary, two-tier or even
one-tier applications are possible.
Web-based CommunitiesAn important aspect of any Web application is
that it creates a virtual community of people of people from potentially all
over the world who share something in common, whether it be photography,
shopping for books, trading stocks, or working for the same company or in the
same field. Consequently, one of the greatest powers of the Web is the way it
enables people to interact. On many online bookstores, for example, you can
share your opinion on a particular book with thousands of other people, and
your opinion can persist for years after you've bought the book (assuming the
site stays in business).
The types of collaboration that you can have over the web are too numerous
to name here, so our discussion will focus on one of the most pervasive and
basic forms of web collaboration, the discussion forum. Forums, or bulletin
boards (bboards) are the essential component that turns a web site into a web
community, and they are also one of the simplest to understand.
A J2EE discussion forum
Suppose we want to build a Web-based discussion forum using J2EE
components. The first step in any software design process is defining
requirements for the application; informally, we define a "discussion forum"
to consist of a series of "messages," where a message is some textual content
with some associated data such as the author, the date posted, and a subject
line. We would like our forum application to allow users to do the following
things:
- post a new message
- read an existing message
- list all available messages
- reply to messages posted by other users
The user interaction with the discussion forum will be via the Web (HTML
over HTTP). The rest of our example will focus on two interactions, listing
messages and reading a single message.
The web page to list available messages might look like this to an
end-user:
Java Discussion ForumList messages
|
The web page to show a single message might look like this:
Java Discussion ForumList
Messages | Read Message
Subject |
Test message |
Author |
Joe User |
Date Posted |
1/23/2001 |
This is a test message. I'm just posting this to demonstrate how we
can write a discussion forum with J2EE.
| Now that we know what we want our
discussion forum application to do and look like, we can start designing and
implementing the application. This means we have to determine how we're
representing and storing messages, and how we're going to retrieve them and
display them over the Web.
Object design
The basic data structure in our forum application is a message, so we'll
define a Java object skeleton to represent it, with "get" methods
corresponding to its public attributes:
package forum;
public class Message {
// return the content of a message body
public String getBody();
// return the message subject
public String getSubject();
// return the date posted
public String getPostDate();
// return the name of the author
public String getAuthorName();
}
We also need to represent a list of messages:
package forum;
public class MessageList {
// return the number of messages in the forum
public int getMessageCount();
// return a message at the given index
public Message getMessage(int i);
}
Database design
Web sites that need to store data persistently and reliably generally use a
relational database product (RDBMS) like Oracle or PostgreSQL for their data
storage. RDBMS'es have a lot of nice properties that make them well-suited for
web applications and multi-user systems. It frees application authors from
worrying about transaction isolation, and ensures that data can always be
restored to some valid state even if the power to the server is cut at a
critical moment. Imagine what could happen if you saved all your forum
messages to an ASCII text file and the server crashes when the message is half
written, or if two people try to post a message at the same time.
Let's assume that we're integrating our message forum into an existing data
model to support a multi-user web site. So we already have a table in place to
represent users:
CREATE TABLE USERS (
user_id integer primary key,
first_name varchar(100),
last_name varchar(100),
...
);
Next we'll create a table to represent the set of
messages stored in our discussion forum:
CREATE TABLE MESSAGES (
message_id integer primary key,
author_id integer references users(user_id),
post_date date,
subject varchar(100),
-- simplification: assume "short" messages so we don't need to use CLOB
body varchar(4000)
);
Implementation: Putting it togetherNow that we know what objects we
need and how we're going to store them in the database, we're ready to start
using J2EE components to put it all together. We can build a first pass of
this system using just servlets, JSP, and JDBC, each of which pre-dates the
full J2EE specification. Here we will describe each API in detail.
Java Database ConnectivityJava Database Connectivity (JDBC) is an API
for connecting Java classes with relational databases. It defines an interface
for connecting to the database, issuing SQL statements, and parsing results.
Database and application server vendors generally provide JDBC drivers, which
are database-specific implementations of the JDBC API. A different JDBC driver
must be used for each database product.
Before the J2EE release, JDBC was part of the standard Java API, in package
java.sql . The J2EE includes an enhanced JDBC interface, in
package javax.sql . Some of the additional features include
scrolling cursors, which allows random access to the rows returned from a
query; batch updates; and a standard API for connection pooling.
This code shows how we can put JDBC into the Message and MessageList class
we defined above to retrieve messages from persistent storage:
Listing: Message.java
package forum;
import java.sql.*;
import forum.util.ConnectionManager;
/**
* Represents a message in the forum and retrieves it from persistent
* storage.
**/
public class Message {
// private data members
private String m_id;
private String m_body;
private String m_subject;
private java.util.Date m_date;
private String m_author_name;
private static java.text.SimpleDateFormat df =
new java.text.SimpleDateFormat("M/d/yyyy");
/** no-argument constructor */
public Message () { }
/**
* Create a new Message object from the
* input ResultSet. The ResultSet must be pointing to a valid row, and
* must contain fields named subject, post_date, first_name, and
* last_name. The "body" field is optional because we might not always
* want to pull in the body content (e.g., if we're pulling in lots
* of message headers to show in list format).
*
* @exception throws java.sql.SQLException if the input result set doesn't
* contain the required fields.
*/
public Message(ResultSet rs) throws SQLException {
fillAttributes(rs);
}
/**
* fill attribute values for this Message object from the
* input ResultSet. The ResultSet must be pointing to a valid row, and
* must contain fields named subject, post_date, first_name, and
* last_name. The "body" field is optional because we might not always
* want to pull in the body content (e.g., if we're pulling in lots
* of message headers to show in list format).
*
* @exception throws java.sql.SQLException if the input result set doesn't
* contain the required fields.
*/
private void fillAttributes(ResultSet rs) throws SQLException {
// loop over available columns to set optional attributes
ResultSetMetaData rsmd = rs.getMetaData();
for (int i = 1; i <= rsmd.getColumnCount(); i++) {
String column = rsmd.getColumnName(i).toLowerCase();
if (column.equals("body")) {
m_body = rs.getString(i);
}
if (column.equals("message_id")) {
m_id = rs.getString(i);
}
}
// mandatory attributes
m_date = rs.getDate("post_date");
m_author_name = rs.getString("first_name") + " " + rs.getString("last_name");
m_subject = rs.getString("subject");
}
/**
* Create a new Message object with attributes values from a row in the
* database.
* @param messageId the value of the message_id column to pick out of
* the MESSAGES table
*/
public Message(String messageId) throws SQLException {
this();
m_id = messageId;
setMessageId(messageId);
}
/**
* Fills this Message's attributes with values from the database.
* @param messageId the value of the message_id column to pick out of
* the MESSAGES table
*/
public void setMessageId(String messageId) throws SQLException {
// hand-waving; assume connection management is centralized
Connection conn = ConnectionManager.getConnection();
PreparedStatement ps = conn.prepareStatement
("select messages.*, first_name, last_name "
+ "from messages, users "
+ " where message_id = ?"
+ " and author_id = user_id");
ps.setObject(1, messageId);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
fillAttributes(rs);
}
conn.close();
}
/** @return the content of a message body */
public String getBody() {
return m_body;
}
/** @return the message subject */
public String getSubject() {
return m_subject;
}
/** @return the date posted */
public String getPostDate() {
return df.format(m_date);
}
/** @return the name of the author */
public String getAuthorName() {
return m_author_name;
}
}
Listing: MessageList.java
package forum;
import java.sql.*;
import java.util.ArrayList;
import forum.util.ConnectionManager;
/** A list of forum messages. */
public class MessageList {
private ArrayList m_list;
/**
* Creates a new MessageList by querying the database.
*/
public MessageList() throws SQLException {
m_list = new ArrayList();
// hand-waving; assume connection management is centralized
Connection conn = ConnectionManager.getConnection();
// query all fields except message body.
PreparedStatement ps = conn.prepareStatement
("select message_id, subject, post_date, first_name, last_name "
+ "from messages, users "
+ " where author_id = user_id");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
m_list.add(new Message(rs));
}
conn.close();
}
/** @return the number of messages in the forum */
public int getMessageCount() {
return m_list.size();
}
/** @return a message at the given index */
public Message getMessage(int i) {
return (Message)m_list.get(i);
}
}
There are many good tutorials for JDBC out there so
we won't explain the code above in detail. We're assuming that we have a
ConnectionManager class, which gives us JDBC Connections on demand. We'll
nearly always want to centralize the details of connecting to the database
(users, passwords, database URLs), and often this centralization comes with
the use of an off-the-shelf connection pool.
One interesting thing to note above is the public
Message(ResultSet) constructor. This allows us to create a new
Message that takes its attribute values from an already-open query, which is
useful if we're not creating Messages one at a time, but rather creating
multiple Messages from a query already opened in another object like
MessageList.
Another interesting thing to note is that we took the code for opening the
database query out of the body of the Message(String messageId)
constructor and put it into a public method called setMessageId .
This allows us to instantiate a Message as a "bean" from a JSP page (more on
that later), because bean classes are always instantiated via the default
no-argument constructor; querying the database is now a side effect of setting
the messageId property on a Message bean.
Also note that we query for the raw date and format using Java's
SimpleDateFormat class, instead of using any available RDBMS
functions for doing the same (e.g., Oracle's to_char ). This makes
our date formatting more flexible should we want to use a different date
format depending on the end-user's locale; it also makes our Java object more
re-usable because it is less dependent on any one vendor's RDBMS syntax.
Now that we have working classes for retrieving messages from the database,
we're ready to start serving them over the web using Java servlets and JSPs.
Java ServletsJust as a Java applet is an application that runs in a
web client, a servlet is a Java class that runs in a web server. Servlets are
similar to CGI programs; they handle an incoming server request, do some
server-side processing, and return a response; the servlet API specifies a
standard API for getting information from the request, like the URL, cookies,
and form variables.
Each Java servlet request runs in its own thread under the same Java
virtual machine (JVM) process; processing all requests under the same process
allows servlets to save server-side state between requests, since all servlet
requests share the same memory address space. All of the usual thread-safety
pitfalls apply to any servlets that store state between requests.
The servlet specification also includes a standard convention for deploying
servlet classes and mapping them to URLs, through a deployment
descriptor file (web.xml ). A set of servlets, web pages, and
the classes they use, along with the deployment descriptor, comprise a web
application or "webapp." This standardized deployment layout is essential
for allowing cross-compatibility between application servers; you can develop
an application on Tomcat and then deploy it on WebLogic, for instance.
WebLogic may have a more sophisticated administration interface than Tomcat
but it uses the same web.xml deployment descriptor and the same file layout
under the hood.
The following code is a servlet that will read a message from the database
and display it to the user's browser, taking the message_id field
from the messageId URL variable:
package forum;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class ShowMessage extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException {
Message m = null;
try {
m = new Message(req.getParameter("messageId"));
} catch (SQLException e) {
// wrap database exception to show standard error page
throw new ServletException(e);
}
resp.setContentType("text/html");
resp.setStatus(200);
PrintWriter out = new PrintWriter(resp.getWriter());
out.println("<h2>JSP Discussion Forum </h2>");
out.println("<a href=\"message-list\">List messages</a> | View message");
out.println("<hr>");
out.println("<table>");
out.println("<tr><th>Subject</th><td>" + m.getSubject() + "</td></tr>");
out.println("<tr><th>Author</th><td>" + m.getAuthorName() + "</td></tr>");
out.println("<tr><th>Posted on</th><td>" + m.getPostDate() + "</td></tr>");
out.println("</table>");
out.println(m.getBody());
out.close();
}
}
The above code creates a Message using the messageId URL
variable; it then generates HTML output to the user's browser. To make the
above example code work, you'd need to create a webapp with all the necessary
class files (Message.class, MessageList.class, and ShowMessage.class) under
the webapp's WEB-INF/classes/forum directory (because all the
classes are in the Java package "forum"); and, in the deployment descriptor
(WEB-INF/web.xml) map a URL to the forum.ShowMessage class with the following
lines:
<servlet>
<servlet-name>show-message</servlet-name>
<servlet-class>forum.ShowMessage</servlet-class>
</servlet>
<servlet-mapping
<servlet-name>show-message</servlet-name>
<url-pattern>/message-show</url-pattern>
</servlet>
Java Server PagesThe first thing most readers probably noticed about
the above example is that it's cumbersome to write web applications
exclusively with servlets. Generating HTML output as string literals is
verbose; and you can't change the HTML output without editing, compiling, and
deploying a Java class file. It's also cumbersome to add a new servlet to a
running server, since you'd have to edit the deployment descriptor and often
restart the servlet container.
Enter Java Server Pages (JSP). JSPs look like HTML pages that contain
snippets of Java code to be executed on the server, but they are really just
shorthand or syntactic sugar for servlets. The JSP container translates them
as needed to Java servlet classes, and compiles the resulting Java code. Since
each page is a new file on disk, and the .jsp files are in the same place as
static .html files, we don't need to edit the deployment descriptor to add a
new JSP page.
We could write the above servlet as a JSP:
<@ page import ="forum.Message" %>
<% Message m = new Message(req.getParameter("messageId")); %>
<h2>JSP Discussion Forum </h2>
<a href="message-list">List messages</a> | View message
<hr>
<table>
<tr><th>Subject</th><td><%= m.getSubject() %></td></tr>
<tr><th>Author</th><td><%= m.getAuthorName() %></td></tr>
<tr><th>Posted on</th><td><%= m.getPostDate() %></td></tr>
</table>
<%= m.getBody() %>
JSPs make it easier to separate content and presentation since the HTML
markup is out in the open, and the default state in a JSP is HTML output with
escapes into Java code instead of the other way around. There are different
notations for executing Java statement blocks (<% ... %>) and
interpolating the values of Java expressions into the output (<%= ...
%>).
J2EE also includes an API for creating new JSP tags and tag libraries. The
syntax for user-defined tags is also legal XML, so the procedures that handle
user-defined JSP tags serve a similar purpose to XML style sheets (XSLT). This
allows other J2EE application components, such as EJBs, to represent complex
data structures in XML, leaving the presentation-level HTML rendering to the
JSP tag extensions.
One such tag library included by default as part of the JSP standard
provides a way to instantiate and access Java classes within JSP without
writing any Java code directly; this assumes that the Java class that you're
accessing is a "bean." ("Beans" are just Java classes that follow a standard
naming convention for the methods that get and set their properties.) The
following code example shows the above servlet re-written entirely with JSP
tags, with no explicit Java code:
<jsp:useBean class="forum.Message" id="msg" scope="page">
<jsp:setProperty name="msg" property="*"/>
</jsp:useBean>
<h2>JSP Discussion Forum </h2>
<a href="message-list">List messages</a> | View message
<hr>
<table>
<tr><th>Subject</th><td><jsp:getProperty name="msg" property="subject"/></td></tr>
<tr><th>Author</th><td><jsp:getProperty name="msg" property="authorName"/></td></tr>
<tr><th>Posted on</th><td><jsp:getProperty name="msg" property="postDate"/></td></tr>
</table>
<jsp:getProperty name="msg" property="body"/>
In this example, we are implicitly creating an
instance of a Message with the jsp:useBean tag; and each
jsp:getProperty tag translates into a call to
msg.getXXXX (note that when property="foo" is
specified, the first letter of the property name is capitalized in the
corresponding method name, e.g., getFoo ). The most interesting
piece of magic is that jsp:setProperty property="*" introspects
into the class, and calls the corresponding setXXXX method, if
available, for every URL or form variable.
It is an ongoing debate whether JSP or servlets (combined with some other
templating system to break out static HTML) are better, and most web
applications use some combination of the two, since servlets can forward
requests on to JSPs and vice versa. A common pattern is using a controller
servlet to perform tasks common to a group of URLs (e.g.,
/path/* ), which does some common processing like user
authentication and then dispatches to a JSP to display a page.
Other J2EE APIsSo far we've only scratched the surface of J2EE with
JDBC, servlets, and JSP. And if we're building strictly a Web-based system,
that may be all we need to use. But there are many other APIs in J2EE that are
also useful, depending on the class of problem.
Enterprise Java BeansThe Enterprise Java Beans (EJB) API is a major
component of J2EE; EJB, servlets, JSP, and JDBC together form the core of the
J2EE APIs. is a component architecture for distributed Java applications in
heterogeneous computing environments. EJBs are JavaBeans classes that run
inside an "EJB container," like servlets are Java classes that run inside a
servlet engine. The EJB container is generally part of a comprehensive J2EE
application server product, which will also include a web server, servlet
engine, and JSP container.
EJBs are instantiated and accessed via Remote Method Invocation (RMI), so
you can call methods on objects from an application regardless of whether the
physical object lives in the same JVM or not. Since RMI in J2EE runs over the
Object Management Group's Internet Inter-ORB Protocol (IIOP, see http://omg.com ), EJBs may also be accessed from
non-Java client applications that are CORBA-compliant.
EJB provides a uniform standard for building a layer of abstraction between
business logic and enterprise information systems, which include RDBMS systems
like Oracle, Sybase, Microsoft SQL Server, etc.; ERP systems; legacy mainframe
systems; and others. EJBs also can be used to build a layer of abstraction
between server-side processing and client-side presentation. An EJB may be
invoked from a stand-alone "thin" Java client application, a Java servlet, a
JSP, or a CORBA application.
EJB FlavorsThere are two flavors of EJBs: entity beans and session
beans. Session beans do not have any persistent state. They may have temporary
state, though; as the name implies, session beans are generally used to keep
track of objects that are valid for a particular user session only, such as
shopping carts or a temporary workspace. Session beans may access the same
enterprise information systems as entity beans, though, and may retrieve
entity bean instances.
Entity beans are persistently-stored objects. They can be thought of as
object views of a row in a database whose properties correspond with column
values. Entity beans that implement the EntityBean interface must
provide methods for managing persistence which correspond to, and generally
issue, the SQL INSERT , DELETE , SELECT ,
and UPDATE statements.
Entity bean persistence may either be managed by the bean itself, or by the
EJB container. When persistence is bean-managed, the bean author must write
the code to interface with the persistent store. Some EJB containers, though,
support container-managed persistence; and it is possible to provide
deployment tools to automatically generate SQL code for the above EJB methods.
It is important to remember, though, EJB is primarily for making
distributed objects; it is not an API for object-relational mapping despite
its support for persistent objects. As a general rule, EJB is not useful in
cases where there is no use for RMI or remote procedure calls. If a particular
solution does not require distributed objects, then EJB can add significant
complexity and cost without providing any truly new functionality.
Applying EJB to the discussion forum applicationIn the discussion
forum example, we could implement the Message class as an entity
bean, and MessageList as a session bean. The EJB API for entity
beans is a standardization so that classes like Message that
access the database and map columns to object properties can be manipulated in
an application-independent way. This allows for the possibility of RAD tools
that can automate the generation of classes like Message .
EJB is really only useful in the discussion forum example, though, if we
want to access Message object instances on a different JVM or
machine than that which does the database query. For example, suppose we were
to extend the discussion forum application so that messages would be viewable
over both the web and from a standalone "thin" Java client application that
doesn't connect to the database directly. A Message entity bean's methods,
including the database queries, would run on the application server as they do
in the web-only system; but the thin client can call accesor methods on the
Message (actually, a stubbed interface) just as if it were an object local to
the client.
Java Transactions
The J2EE includes the Java Transactions API (JTA) for performing
distributed atomic transactions with enterprise information systems. This
allows the programmer to ensure that a series of operations will either be
entirely completed, or not done at all. Half-completed transactions are not
allowed.
While most RDBMS packages include facilities for transaction management,
they are limited to managing operations with that one RDBMS. The JTA can
provide distributed transactions that consist of operations on multiple
enterprise information systems.
Consider the following scenario:
- Customer logs in to RDBMS-powered, e-commerce web site
- Customer places order, order information stored in RDBMS
- Order information also updated in legacy order-fulfillment system
In the preceding example, should something go wrong with the
order-fulfillment operation, the customer's order should not be recorded in
the RDBMS. The JTA allows these operations to be treated as a single atomic
operation.
CORBA CompatibilityJ2EE includes two-way compatibility between Java
and CORBA objects; either one may call the other. The Java 2 Standard Edition
(J2SE), a subset of J2EE, provides an Interface Definition Language (IDL)
compiler, which generates Java stubs for calling remote CORBA objects from
Java applications; and an Object Request Broker (ORB), which allows Java
classes, including EJBs, to be called remotely from CORBA.
Java Naming and Directory InterfaceThe Java Naming and Directory
Interface (JNDI) is an API for maintaining directories of name-to-object
bindings. It may be used as the primary means of storing user information in a
Java enterprise application, or it may be used in a helper role to the other
J2EE APIs for locating remote objects. For example, a developer who
instantiates an EJB within a client application might only know the name of
the EJB's remote interface and nothing about the actual class which will
handle the transport between the client application and the EJB container.
JNDI is used to look up the actual class given an interface name.
JNDI supports LDAP (Lightweight Directory Access Protocol), an industry
directory standard. This allows Java clients to connect to LDAP-compliant
directory servers (e.g., Netscape Directory Server), and other LDAP clients to
connect to Java enterprise applications. JNDI directories can issue events
when changes are made to directory bindings. Other objects can register
listeners to receive these events.
Java Message Service (JMS)JMS is an asynchronous messaging service
for connecting different Java applications, or a distributed application that
runs on more than one virtual machine. Asynchronous messaging differs from the
synchronous messaging used in RMI and EJB, and has different applications. The
JMS API supports transactional message queues, which are implemented by
various J2EE applications servers.
JavaMailJavaMail is an object-oriented API for parsing, constructing,
and sending MIME e-mail over the Internet. It consists of abstract classes for
sending e-mail messages, representing MIME multipart messages, and
representing e-mail folders. JavaMail would be useful in the discussion-forum
example for sending out e-mail alerts to subscribing users when new messages
are posted.
XMLXML (see http://www.xml.org) is
not new, nor is it officially part of J2EE, but its role in enterprise Java
applications is significant enough that it merits discussion here.
XML's role in enterprise applications stems from its utility in B2B
information exchange. Most collaborative commerce systems will probably make
at least some use of XML as a format for interchanging structured data. Also,
XML parsing is related to the JSP tag extension mechanism.
To facilitate the use of XML within Java applications, J2EE defines a
standard interface for Java XML parsers. Interfaces are provided for both
Simple API for XML (SAX) parsers and Document Object Model (DOM) parsers. SAX
parsers are event-driven; they call methods in a specified document handler
object when XML elements start or end, or when character data sections are
encountered. DOM parsers create an in-memory tree of objects corresponding to
the structure of the XML document.
Future directionsJ2EE can be used for building community-based Web
sites and collaborative Web applications. Java servlets, JSPs, and JDBC are
the most important J2EE building blocks of a Web-based community, though the
EJB API may occasionally be appropriate for interoperability with other
systems where interaction over HTTP is cumbersome.
We showed an example of a database-backed web application that could be
built with J2EE components, using only Java servlets, JSP, and JDBC. Other
J2EE APIs like EJB and JavaMail would become important when we want to add
specific features to the web application, or if we want to access the same set
of business objects from server-side components in the same JVM, or
programatically through remote procedure calls.
One of the greatest promises of the J2EE is the potential for client-server
applications that are independent of their client type. By creating
abstractions for business rules and data models in EJB classes, and using
standard J2EE APIs for accessing them, it is possible to develop the same
application with different client interfaces for Web browsers, handheld
computers, and any future device with a JVM.
asj-editors@arsdigita.com
Reader's Comments
I would be keen to know what Philip's perspective is on J2EE, and most of the items discussed in this page. Has he read this brief article?
It seems Java, especially at the server level has advanced quite a bit since Philip wrote his book. Are software sellers just trying to convince us to use the latest and greatest so they can sell it, or is designing an application around objects a good methodology for application development, maintenance, and extensibility.
-- Philip Jensen, October 7, 2000
Indeed taking SQL queries out of JSP pages and putting them into compiled Java classes robs the page authors of the full expressive power of the SQL language. But isn't that a necessary (and desirable) consequence of building a structured system where the various functions (presentation, database access, ...) are layered and kept cleanly separate from each other? JSP as I understand it is meant to take part of the presentational side of things; it seems therefore quite natural to me that SQL subtleties should not be an issue to the JSP programmer. In other words, putting structure in a system has to be done at the expense of removing some degrees of freedom somewhere.
Of the two alternatives to the problem of retrieving information from a message bean, the first one (having a getXXX method for each attribute) seems perfectly acceptable to me. The fact that two queries are needed instead of one is beside the point. It is an efficiency issue croping in through the back door. If relational databases were really smart, it wouldn't have to be the case. But even if we want to compensate for our database weakness, a hypothetical message bean is the perfect place to do so. We want the bean to be easy to use with individual getXXX methods for each of the attributes and we want it to be efficient. One way to do it is to make sure that when the bean retrieves one (or many) rows from the messages table through a getXXX method call, it also gets all the columns which are likely to be needed and caches them. After a certain amount of time (or when memory becomes scarce), the bean drops the cached results. Hence the bean can
hide these gruesome details from the bean consumer.
-- Guillaume Barreau, October 18, 2000
Embedding sql in jsp is simply insane. It makes schema
evolution virtually impossible, and
performance analysis extremely difficult (since you're reduced
to browsing through a bunch of jsp's with slightly different
variants of the same query looking for the exact one thats
hammering your poor database). Also, it implies that either
your database engineers are also UI experts, or vice versa. Neither
of these assumptions scale well in the real world.As for the performance issues, discussed in both the comments
and the article - they both overlook the most important benefit
of having an object wrapping around the sql - caching. Not only
do you get code reuse, but you can also get actual object reuse.
The bboard example is an excellent candidate since the objects are
largely read only. Note that its important to distinguish between
caching objects in the application, vs. the use of the RDBMS cache.
While you will seem some benefits from cache hits in the database,
it turns out that this will quickly be swamped by networking overhead
if you make multiple distinct requests for different columns in the
same row. With the exception of message bodies, where the cost
of getting the column is relatively high, you're probably better
off doing fewer queries that return multiple rows and caching the
results locally than fetching just the row you need just when you need
it. Minimizing round trips to the database is an important
performance issue ...
-- Lee Schumacher, November 10, 2000
I agree that having SQL in presentation layer code (JSP) is a bad idea for anything except small, simple, quick'n'dirty hack projects.Normally you would want presentation-layer objects to talk to application-layer objects which manage database access and any complex processing logic. That allows you to have much better control over what database access your system is doing, which is better for reliability and maintainability. Ãn hig-traffic systems this also lets you make design deicsions about whether you want to allow objects to manage their own persistence by accessing the database at will, or whether you want to implement some kind of application cacheing scheme. (Caching or not depends on things like whether you have performance issues accessing the db, which would argue for lots of cacheing, or whether your db is getting heavy update traffic from multiple soruces, which would argue against it. There are obvious dangers in trying to get too clever with application-level cacheing if you are operating in environments with a lot of concurrent read and update traffic, and if it matters to you that your read users get to see the most current updates. At this point you start having objects telling each other when to refresh cache, and will drift towards reimplementing a half-baked version of the DBMS's cacheing system - except that Oracle (or IBM or Sybase or whoever) have put tens of man-years of work into designing way more efficient and reliable algorithms than you are ever likely to. The SQL-in-JSP examples aside, though, I found this a useful and clear overview of a topic that is surrounded with a lot of buzz and confusion if you're not intimately familiar with the technology.
-- Alan Little, December 13, 2000
We are presently dealing with the SQL-in-JSP UI-vs-DB seperation issue by having two seperate JSP pages for each URL (different file extensions); one sets up the database queries (it's generally just Java code) and one defines the visual display (using our ATS templating system to deal with displaying the results of the database query setup page). My personal experience with this is that it is much simpler to code the queries because you can reload the page to see an update, rather than having to recompile a class, shutdown the webserver, and restart the webserver. You can still put queries into Java classes if they are truly general-purpose, but the "full expressive power" argument and desire to use as few DB queries as possible generally results in this not being the appropriate thing to do (i.e. you could write a one "getItem" query and reuse it on another "show all items" page by calling it a hundred times to get a hundred items; but then you've just done a join in your programming language rather than in the database, robbing your code of efficiency via huge database overhead. Always do the joins in the database + don't ask for more information than you need = unfortunately less chance to reuse queries than you might wish for) End result: UI authors learn a few new tags and they never have to know anything about SQL. SQL/Java authors write a "page contract" for the UI authors and then their SQL+logic is isolated from the presentation. Nobody has to recompile and shutdown the webserver to see changes, although they can go that route if it's appropriate for the task at hand. The ACS has had the database queries mostly in the pages to be served for several years now and it's worked out quite well. I don't think anyone could argue that the ACS is a small simple hack.
-- David Eison, December 16, 2000
Related Links
|
|
|