Newsletter sign-up
View all newsletters

Sign up for our technology specific newsletters.

Enterprise Java
Email Address:

AjaxChat: Chatting the AJAX Way, Part 2

Start building your own AJAX-based chat room

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone

Now that we have examined all the files that effectively make up the presentation of AjaxChat, we'll now begin to explore the server-side code, starting with a simple class, AjaxChatConfig.

AjaxChatConfig.java

The AjaxChatConfig is a typical JavaBean with fields that correspond to the possible parameters in app-config.xml. It also contains what is my typical toString() method that I use in most beans:

 /**
* Overridden toString method.
*
* @return A reflexively built string representation of this bean.
*/
public String toString() {

String str = null; StringBuffer sb = new StringBuffer(1000); sb.append("[" + super.toString() + "]={"); boolean firstPropertyDisplayed = false; try { Field[] fields = this.getClass().getDeclaredFields(); for (int i = 0; i < fields.length; i++) { if (firstPropertyDisplayed) { sb.append(", "); } else { firstPropertyDisplayed = true; } sb.append(fields[i].getName() + "=" + fields[i].get(this)); } sb.append("}"); str = sb.toString().trim(); } catch (IllegalAccessException iae) { iae.printStackTrace(); } return str; } // End toString().


This code uses reflection to display the beans' contents, which is very handy during debugging. Note that the fields and accessor methods are all static. This is an easy way to have a global settings cache in effect. You may notice that the mutator methods are not static. This is because Commons Digester is used to populate this object, and Digester requires that it be able to instantiate the object. Therefore, while there is probably no harm in making them static, there is no need either. It is therefore a little safer to make them nonstatic, so that if someone were inclined to improperly populate this object manually, they would have to instantiate it to do so.

LobbyActionForm.java (and all the ActionForms essentially)

Next up, we'll look at the one ActionForm in AjaxChat, LobbyActionForm. As you will recall, a Struts ActionForm is really just a simple JavaBean that happens to extend from a specific class (ActionForm). It is, like our AjaxChatConfig object, just a collection of fields, accessors, and mutators. Also, again we see the same toString() method as before.

MessageDTO.java

AjaxChat makes use of three DTOs (data transfer objects) to pass information around. The first of these is the MessageDTO.

Notice here that the postedBy member is actually of type UserDTO, which we'll see in a moment. This DTO contains all the pieces of information that together make up a message posted to a chat room, including who posted it, when the post was made, and of course the text of the message. Otherwise, it is once more just a simple, perfectly typical JavaBean.

RoomDTO.java

The next DTO is the RoomDTO. This DTO does a little more than other DTOs, as we'll see. The reason this DTO is slightly different than the rest is that when AjaxChat starts up, an instance of this class will be instantiated for each room configured, and that object will be stored in the AjaxChatDAO that we'll look at shortly. It is not simply a container for pieces of information, as most DTOs are. Instead, you could almost think of this DTO as more of a domain object than anything else because it contains some functional code as well. For instance, when a user is added to the room, the addUser() method is fired:

 /**
 * Adds a user to the list of users chatting in the room. The user WILL NOT
 * be added if they are already in the collection, which should deal with
 * the user clicking the Refresh button on their browser.
 *
 * @param inUser The user to add to the list of users chatting in the room.
 */
public void addUser(UserDTO inUser) {

if (log.isDebugEnabled()) { log.debug("RoomDTO addUser()..."); } boolean userAlreadyInRoom = false; for (Iterator it = users.iterator(); it.hasNext();) { UserDTO user = (UserDTO)it.next(); if (user.getUsername().equalsIgnoreCase(inUser.getUsername())) { userAlreadyInRoom = true; } } if (!userAlreadyInRoom) { if (log.isDebugEnabled()) { log.info("Adding user to room: " + inUser); } users.add(inUser); Collections.sort(users); }

} // End addUser().


This method checks to see if the user is already in the room and, of course, does not allow them to join the room if they are. This should generally not happen; the check is performed because it theoretically could under certain very unusual circumstances. This would require certain things happening with precise timing, so it is extremely unlikely. Still, the check is performed to ensure a user is not put in a room twice. This method also sorts the list of users once the user has been added to the collection of users chatting in the room so that when it is displayed, it is in alphabetical order (better to do this sort only when someone joins the room to avoid the overhead of doing it with each user list update request).

Just in case you are new to Jakarta Commons Logging (JCL), let me mention that the following code is how logging is done in JCL:

 if (log.isDebugEnabled()) {
   log.debug("RoomDTO addUser()...");
} 


The if check you see first is called a "code guard." The reason for this is that logging statements, in some cases, should be avoided if not necessary. One of the criteria that determines that necessity is often whether the application is being run in "debug" mode. So, logging statements should generally be wrapped in a code guard to see if the application is running at some specific logging level.

When I said that logging should be avoided in some cases, interestingly, this is not one of those cases! When log.debug() is called, it will do the equivalent of log.isDebugEnabled(). So, you may think that is less efficient because the check will be performed twice here, and you would be right. However, imagine the following logging statement:

 log.debug("User: " + UserDTO.getName() + ", Age: " + UserDTO.getAge());  


If you were to execute that without a code guard, the check would only be performed once. However, the problem is that regardless of whether the message actually gets logged—that is, whether the check returns true or false—the string concatenation will always be performed first. Therefore, even if the application is not in debug mode, which means the message would not be logged, you still incur the overhead of the string construction, and that overhead far exceeds that of doing the check twice. So, wrapping such a line in a code guard means that if the message is not going to be logged, that will be determined before the message is constructed, making it considerably more efficient.

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comment
Login
Forgot your account info?
Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources