Requirements and goals
Chat rooms have been with us for a very long time now. In fact, it is chat rooms, those gathering places where you can talk in real time with your fellow computer users, that draw many people to the Internet in the first place (email tends to be the biggest draw, but chat rooms are not too far behind for many people).
If you think about what a chat room application must require, it becomes clear that we'll need a server component. There has to be some broker in between all the chatter that deals with keeping track of the various chat rooms available, who is logged in and chatting in what room, and those types of system-level considerations. More important, though, is some arbiter of messages—a way for all the people currently chatting in a given room to see the various messages.
In a "real" chat application, such as those you might find on AOL, for instance, it is likely that the server actually pushes new messages out to the users in the room. That way, there is no delay between when someone says something and when everyone else in the room sees it.
If you were going to build a purely HTML-based chat application, as we are about to do, you'd have to consider all of these points. However, because the Web is based on a pull model of client-server interaction, ignoring things like applets and such, you clearly need to go about things a little differently. Could you have a meta refresh tag on a page that periodically asked the server for any new messages? Yes, but then you would be redrawing the entire screen each time, which would be rather inefficient, especially if you built it with something like JSP (JavaServer Pages) or another dynamic rendering technology where the server would be responsible for that redrawing.
There must be a better way, and of course there is: AJAX!
The AjaxChat application will have a number of requirements based on these, and a few others, as follows:
- The application must support multiple users and multiple rooms. We won't enumerate any specific scalability requirement except to say that a reasonably sized group of friends should be able to chat simultaneously, so something in the 10-user range should do fine.
- We have seen a number of solutions that use servlets on the back end, so this time, we'll do something a little more interesting and robust and build AjaxChat using Struts.
- We want to be able to adjust the font size of messages displayed in the room we are chatting in, to allow for people (like me!) with bad eyesight to have an easier time of it.
- Just for fun, we want the ability to show our messages in one color and other people's messages in another.
- The server will need to properly deal with things like duplicate users and users who do not properly log off. We won't, however, require a login per se—that is, user accounts will not be created and persisted.
- There should be an option to clear the "history" in a room so that a user can have a clean display at any time.
This should be a fun project! Let's now figure out how we are going to accomplish all that we have set out for ourselves.
Visualizing the finish line
Creating AjaxChat, we first should know what we're building, so what follows is some screenshots and description of AjaxChat. The application essentially consists of three distinct screens: the "login" screen, the list of rooms (called the lobby), and the chat room itself.
Figure 1 shows the login screen. This screen is pretty simple, although we do have some eye candy in the form of a shadowed inset text box and a metallic-looking button. This screen is just a greeting and a place to enter a username. It is not a security login, mind you; it is simply a way for users to give themselves a name to chat under.
The next screen, the room list screen, is shown in Figure 2. This screen shows all the available chat rooms and how many users are chatting in each. It also provides a logout button. This serves to clear out the user's session and ensures that they do not appear to still be chatting in any rooms.
Lastly, we come to the meat and potatoes of AjaxChat: the screen representing a chat room (Figure 3).
The chat room screen tells us what room we are chatting in and shows a list of the users in the room with it. It provides a place to enter our messages, and a place to see all the messages posted to the room since we entered it. We also have some bells and whistles in the form of two icons for increasing or decreasing the size of the font the message scroll is seen in, as well as select boxes to change our own message's color and the color of all the messages of all the other chatters in the room. Naturally, we have a way to exit the room, and we also have a button that clears the chat history so we can have a nice, clean message display.
This screen is laid out using tables. You may wonder why I did not do this by using CSS (Cascading Style Sheets) layout techniques. The simple answer is that those techniques are still in their infancy, and cross-browser issues remain to be worked out. Tables are still my layout option of choice and will be until those issues are worked out. It is possible to do this screen with CSS and no tables, but it would have been fragile depending on which browser you viewed it in. Better to use the technology that is more ubiquitous and well-behaved at this point in time. Besides, for Web applications, CSS layout is somewhat less interesting simply because the layout tends to not change as much. For Websites, where content delivery is the primary concern, CSS layout has much more to offer.
Enough of the tables versus CSS layout debate—let's get into some code!
Dissecting the solution
Download the full source from the Apress Website and follow along here. There is too much code to commit to print here, but I will call out bits and pieces where appropriate; otherwise, downloading and following along is very important.
To begin, let's look at the file layout of the project, as shown in Figure 4.
We see our typical Webapp structure. AjaxChat consists of three JSPs:
- index.jsp, which is our welcome page and where the user "logs in"
- lobby.jsp, which is where the chat room list is
- room.jsp, which, of course, is the JSP for inside a chat room
Interestingly, they go in exactly that order in terms of complexity!
All three of them reference the stylesheet styles.css in the css directory. All three also make use of the buttonBG.gif and textBG.gif images for stylizing buttons. The last two images, zoomDown.gif and zoomUp.gif, are used in the room to provide for text zooming capabilities.
In the inc directory, we find a single file, color_options.inc. This file is included in the room.jsp file when rendered (a server-side include) and contains all the
<option>s for the dropdowns where colors can be selected. Because there are so many of them, having it all in one place is definitely advisable.
The WEB-INF directory contains web.xml, as usual for a Java Webapp, and also contains struts-config.xml, the Struts configuration file. In this directory you will also find app-config.xml, which is a custom configuration file where a few parameters for the application are stored. Lastly, you will find rooms-config.xml, which is the file where the chat rooms available to chatters are stored.
The WEB-INF/classes directory is where the beginning of our server-side Java classes are. We can see that there are quite a few, and we'll of course be going over them all in detail. In brief, however, the classes found in the action directory are our Struts
Actions. The single class found in the actionform directory is our Struts
ActionForm (only one is found here; two others are
DynaActionForms defined in struts-config.xml). The single class in the daemon directory is a background thread that will be used to clean up users when they leave the application. The dao directory contains the single data access object AjaxChat uses. The dto directory contains three data transfer object classes used by AjaxChat to store information about rooms, users, and individual messages. In the filter directory, you will find a single servlet filter used to check whether a request references a valid session as far as AjaxChat is concerned. The listener directory contains a single
ContextListener that is used to initialize AjaxChat.
Finally, the root ajaxchat directory contains a single class that is used to store some configuration information. And, the WEB-INF/lib folder contains all the libraries that AjaxChat depends on, and they are listed in Table 1.
Table 1. The JARs that AjaxChat depends on, found in WEB-INF.lib
The client-side code
We'll start our dissection by looking at the configuration files for this application. Let's begin by looking at web.xml.
The first context parameter,
javax.servlet.jsp.jstl.fmt.localizationContext, tells JSTL (JSP Standard Tag Library) we want to internationalize our messages. It also serves to provide the base filename for the properties file.
Next, we see the
configFile context parameter. This will be used by our
ContextListener to locate the configuration file for the application. In this case, we are telling it to look for app-config.xml in the WEB-INF directory, relative to the context.
The last context parameter we see is
configClass. This is the class that will be populated with our configuration information,
com.apress.ajaxprojects.ajaxchat.AjaxChatConfig in this case.
After the context parameters comes a single filter configuration of the
SessionChecker filter. We'll go into what that filter does later, but for now it is enough to know that it will fire for every request ending in
*.do made to this context.
After the filter configuration comes a
ContextListener configuration. Here we are using the
AppConfigContextListener that can be found in Java Web Parts. This listener allows us to read in a configuration file and populate a config object in various ways. Here we have a very simple configuration, so the default behavior is used: the named class will be instantiated, each element in the config file will be read in, and the corresponding setter in the instantiated object will be called. This listener can deal with more complex config files, as well as provide a default simple bean for storing the configuration information. Have a look; it is a pretty handy part!
The next section of web.xml declares our Struts
Action servlet. Note the
debug parameters. This determines the level of logging Struts will do for us.
After that comes configuration of the session timeout. We set the value so that a session will expire after one minute. This is because a session is not established until after the user "logs in," and after that, we have AJAX calls continually firing every few seconds. So a session should never get close to a minute old. If it does, and the session expires, it likely means the user navigated away from the chat application, and we want to catch that situation as quickly as possible so we can clear them out of any room they were in. A minute is usually the smallest value you can set for a session timeout, although some containers may allow for a finer level of granularity. A minute should work for us though.
Lastly, we configure the welcome page to index.jsp, so any request that comes in that does not reference a particular document (or end in
*.do) will be routed to this JSP, which is our application's starting point.