WILL RESTART ONE FOR SUBJECT &TOPIC chatExistsForSubjectAndTopic = false;actorRestartingCurrentChat = false;} }}// see if actor is a host in chat found with requestedsubject and topic St
Trang 1/** BonForumRobot repeatedly invokes showDocument method of applet.
* It can be used from a frame display to request a different frameset
* It can also be used to continually refresh one frame from another
* The applet parameters are:
* target, document, increment, limit, message and refresh
* <p>For further information visit the open source
* <A HREF=”http://www.bonForum.org”>BonForum Project on SourceForge</A>
* @author <A HREF=”mailto://email@bonforum.org”>Westy Rockwell</A>
boolean refresh = false;
boolean continueRunning = true;
int increment = 0;
int limit = 0;
int counter = 0;
Font font = new Font(“TimesRoman”, Font.ITALIC,24);
public void init() {System.out.println(“init()”);
// get other plugin parameterstarget = getParameter(“target”, “_self”);
document = getParameter(“document”, “”);
increment = getParameter(“increment”, 20000);
limit = getParameter(“limit”, 10000);
message = getParameter(“message”, “BonForumRobot applet”);
refresh = getParameter(“refresh”, false);
// see these debugging messages on the Java ConsolecodeBase = this.getCodeBase();
System.out.println(“documentBase:” + this.getDocumentBase().toString());System.out.println(“codeBase:” + codeBase.toString());
Trang 2target = “_top”;
System.out.println(“changed to forum_error target:” +this.target);
}}}public void start() {// kick off thread to do the dirty worksetBackground(Color.cyan);
System.out.println(“start()”);
if (refresh) {RefreshThread thread = newRefreshThread(Long.toString(System.currentTimeMillis()), codeBase);
thread.start();
}}public void stopRunning() {stop();
}public void stop() {System.out.println(“stop()”);
continueRunning = false;
}public void paint(Graphics graphics) {graphics.setFont(font);
graphics.setColor(Color.black);
if(message.equalsIgnoreCase(“debug”)) {graphics.drawString(messageLineOne,10,20);
graphics.drawString(messageLineTwo,10,40);
}
Trang 3}private String getParameter(String name, String defaultValue){
String retval = getParameter(name);
if (retval == null || retval.trim() == “”){
retval = defaultValue;
}return retval;
}private int getParameter(String name, int defaultValue){
int retval = defaultValue;
String tmp = getParameter(name);
if (tmp != null && tmp.trim() != “”){
try {retval = Integer.parseInt(tmp);
} catch (NumberFormatException nfe) {// don’t do anything
// it’s still assigned to the defaultValue!
}}return retval;
}private boolean getParameter(String name, boolean defaultValue){
boolean retval = defaultValue;
String tmp = getParameter(name);
if (tmp != null){
if (tmp.equalsIgnoreCase(“true”)) retval = true;
if (tmp.equals(“1”)) retval = true;
if (tmp.equalsIgnoreCase(“false”)) retval = false;
if (tmp.equals(“0”)) retval = false;
}return retval;
}public class RefreshThread extends Thread {private URL codeBase;
public RefreshThread(String s, URL cb){
super(s);
codeBase = cb;
}public void run() {String uncachedDocument = “”;
String errorDocument = codeBase.toString() + “ /forum_error.jsp”;messageLineOne = “”;
Trang 4+ limit);
stopRunning();
continue;
}// no, do itcounter++;
String millis = Long.toString(System.currentTimeMillis());
// LATER: we will somehow refresh content only if staleif( (document.indexOf(“forum_error”) > -1)
// We fixup the filename as a unique filename to preventbrowser from
// retrieving the last result for a jsp uri request from
Trang 5its cache.
//
// For example,//
“http://localhost:8080/bonForum/jsp/forum/visitor_joins_chat.jsp#entry47”
// will become something like this:
//
“http://localhost:8080/bonForum/jsp/forum/visitor_joins_chat.jsp#entry47.962066767851.tfe”
uncachedDocument = document + millis + “.tfe”;
System.out.println(“Created name for uncachedDocument:” +uncachedDocument);
}else {// NOTE: applet code must be in subfolder (e.g.,
“applet”) of folder with forum_login.jsp
// so this creates something like
“http://freedom:8080/bonForum/jsp/forum/forum_login.jsp”
uncachedDocument = errorDocument + millis + “.tfe”;System.out.println(“Document not in list, using an errordocument:” + uncachedDocument);
target = “_top”;
message = “Unknown destination, new login required!”;}
if(target.equals(“_top”)) {stopRunning(); // after this loopgetAppletContext().showStatus(message);
}messageLineOne = “loading ”;
messageLineTwo = message;
repaint();
try {getAppletContext().showDocument(newURL(uncachedDocument), target);
} catch(MalformedURLException ee) {System.out.println(“MalformedURLException:” +ee.getMessage());
System.out.println(“MalformedURLException,uncachedDocument:” + uncachedDocument);
Trang 6document = “”; // force errorDocument next time}
messageLineTwo = “”;
}}
} //End of Inner Class}
/** BonForumEngine is the central servlet of bonForum web application
* At present, it implements a chat Its purpose is experimentation
* It is described fully in the book:
* <i>XML, XSLT, Java and JSP - A Case Study in Developing a Web Application</i>,
* by Westy Rockwell, published by <A HREF=”http://www.newriders.com”>NewRiders</A>
* Translation to German published by <AHREF=”http://www.galileocomputing.de”>Galileo Press</A>
* <p>For further information visit the open source
* <A HREF=”http://www.bonForum.org”>BonForum Project on SourceForge</A>
* @author <A HREF=”mailto://email@bonforum.org”>Westy Rockwell</A>
*/
public class BonForumEngine extends HttpServlet {// holds and gives access to the data for the forumprivate static BonForumStore bonForumStore = new BonForumStore();
// ensures nicknames are uniqueprivate static Hashtable nicknameRegistry = new Hashtable();
// logs debugging informationprivate static BonLogger logBFE = null;
// controls logger outputprivate static String logging = null;
// false until logger readyprivate static boolean loggingInitialized = false;
/** Initializes a BonForumEngine instance
* Also sets its logging value from application init param
* Also creates its logger if not done before
Trang 7System.err.println(“BonForumEngine init loggingInitialized:” +loggingInitialized);
System.err.println(“BonForumEngine init loggingInitialized:” +loggingInitialized);
getBonForumStore().setLogging(logging);
System.err.println(“BonForumEngine initgetBonForumStore().setLogging(logging)”);
}}System.err.println(“LEAVING BonForumEngine init”);
}/** Gets the BonForumStore from this BonForumEngine
logBFE.logWrite(System.currentTimeMillis(), sessionId, where,what);
}}/** Processes requests in context of web application rules
* Called from BonForumEngine service method
* Customizes the HttpServlet based engine as a web application
* (a chat in this case)
*
* @param request HttpServletRequest argument from service method
* @param response HttpServletResponse argument from servicemethod
* @param session HttpSession current
* @param bonForumCommand String routes request to next destination
* @return String bonForumCommand parameter, maybe changed by this method,maybe not
String bonForumCommand)throws IOException {BonNode chatNode = null;
Trang 8Object obj = null;
String sessionId = session.getId();
// using sessionId for now, later it will be// replaced with userId, when user manager is implemented
bonForumStore.initialize(sessionId);
// See if bonForumStore instance is bound to this ServletContext// If not, bind bonForumStore so it can be found elsewhere in web app// Then, you can use code like the following from other classes, to// have access to the methods of the bonForumStore:
//
// BonForumStore bonForumStore =// (bonForumStore)application.getAttribute(“bonForumStore”);
(bonForumStore)(pageContext.getServletContext().getAttribute(
// “bonForumStore”));
// }//
// Of course, to use properties, it is simpler to do this:
Trang 9}// bonForumCommand selects the next state of the application// Its value will be used to create a JSP filename.
// If it has no value here, construct one from bonCommand// request parameter, or else, from other request parameters
bonForumCommand = normalize(bonForumCommand).trim();
if(bonForumCommand.length() < 1) {// As a second alternative the engine uses// the bonCommand request parameter// to tell where to forward the requestString bonCommand =
normalize((String)request.getParameter(“bonCommand”)).trim();
if(bonCommand.length() > 0) {bonForumCommand = bonCommand;
}else {// As a third alternative, the engine can use any combination// of one to three other parameters (actor, action, thing)// to construct the bonForumCommand described aboveString actorStatus =
normalize((String)request.getParameter(“actorStatus”)).trim();
String actionStatus =normalize((String)request.getParameter(“actionStatus”)).trim();
String thingStatus =normalize((String)request.getParameter(“thingStatus”)).trim();
if((actorStatus.length() > 0) ||
(actionStatus.length() > 0) ||
(thingStatus.length() > 0)) {bonForumCommand = actorStatus + “_” + actionStatus + “_”+ thingStatus;
// later, trim off leading and trailing underscore chars,
if any
}else {bonForumCommand = “forum_error”;
Trang 10// 1 If the parent is one of the intrinsic system elements then that// element’s name (“bonForum”, “”actors”, “actions”, “things”, “system”,etc.)
// is the key in the nodeNameHashtable for the parent nodeKey
//
// 2 If the parent is not one of the intrinsic system elements// (for example, a “message” element inside the “things” element)// then the key in the nodeKeyHashtable is made up of the following:
// <sessionId> + “_” + <nodeKey.aKey> + “:” <elementName>
// (for example: “54w5d31sq1_985472754824:message”)// NOTE: there is also an option to leave out the nodeKey.aKey portionof
// the key, for a selected list of node names (see ForestHashtable,property
// UniqueNodeKeyKeyList That reduces the size requirements of the// nodeKeyHashtable (for example, by not storing all the message nodeKeykeys)
//
// 3 If the parent is one of certain elements that are loaded into// the bonForumXML (like the “subjects” subtree loaded with the// loadForumXML command, for example), then the nodeKey is in the// corresponding hashtable for that upload
// (For example, subjects elements are in the pathNameHashtable with// a key made from the path from root to the node whose nodeKey issaved
// An example of a subject key is
// String as content to the chat node That same hostKey value will be
Trang 11// is known Having just one method adding nodes does have advantagesthough.
// PROCESSING OF MANY FORUM COMMANDS IS NEXT// This extra bonForum variable is available on most JSP form,// It can be used later for setting options for bonForumCommand, orwhatever
String actorReturning =normalize((String)request.getParameter(“actorReturning”));
if(actorReturning.trim().length() > 0) {session.setAttribute(“actorReturning”, actorReturning);
}else {session.setAttribute(“actorReturning”, “”);
}// Incoming request parameters are used as “bonForum variables”
// (including actor data, GUI settings, chat messages, etc.)
// The parameter values are processed and/or set in attributes
// Most are set in session attributes, but at least two in applicationattributes
// They can also be added to the bonForumXML “database” (chat messagesare, for example)
// These if clauses are roughly prioritized by frequency of access,// and grouped by application state, (except for “_executes_chat”commands)
if(bonForumCommand.indexOf(“host_executes_chat_controls”) > -1 ||bonForumCommand.indexOf(“guest_executes_chat_controls”) > -1) {//handle chatMessagesNavigator
chatMessagesNavigator =normalize((String)request.getParameter(“chatMessagesNavigator”));
if(chatMessagesNavigator.trim().length() > 0) {session.setAttribute(“chatMessagesNavigator”,chatMessagesNavigator);
}// handle chatMessage// If we have a message save it as child of things,// and the messageKey to the chat element
chatMessage =normalize((String)request.getParameter(“chatMessage”));
if(chatMessage.trim().length() > 0) {// add message child to things element
Trang 12// get itemKey from session AttributeString itemKey =
normalize((String)session.getAttribute(“itemKey”));
if(itemKey.trim().length() < 1) {itemKey = “”;
log(sessionId, “err”, “processRequest() ERROR: sessionhas no itemKey!”);
}// NOTE: make sure attribute name=value items are separated by
nameAndAttributes = nameAndAttributes + “ dateStamp=\”” +BonForumUtils.getLastDate() + “\””;
// Try to get hostKey from session Attribute// If it is not there get guestKey instead// LATER: add systemKey to this also!
String actorKeyValue =normalize((String)session.getAttribute(“hostKey”));
if(actorKeyValue.trim().length() < 1) {actorKeyValue =
normalize((String)session.getAttribute(“guestKey”));
if(actorKeyValue.trim().length() < 1) {log(sessionId, “err”, “no hostKey or guestKey formessage!!!!!!!!!!”);
actorKeyValue = “”;
}else {
nameAndAttributes = nameAndAttributes + “guestKey=\”” + actorKeyValue + “\””;
}}
else {nameAndAttributes = nameAndAttributes + “ hostKey=\”” +actorKeyValue + “\””;
}chatNodeKeyKey =normalize((String)session.getAttribute(“chatNodeKeyKey”));
/* THESE ARE DEBUGGING TESTS// test value with escaped quote (xml error)
Trang 13nameAndAttributes = nameAndAttributes + “type=\”te\\\”st\\\”ing\””;
// test unclosed quotes in value (xml error)nameAndAttributes = nameAndAttributes + “ test1=\”” + “hellotest1!”;
NodeKey messageNodeKey = (NodeKey)obj;
// get messageKey as String//String messageKey = messageNodeKey.aKey + “.” +messageNodeKey.bKey + “.” + messageNodeKey.cKey;
String messageKey = messageNodeKey.toString();
// add messageKey to chatnameAndAttributes = “messageKey”;
else if(bonForumCommand.equals(“host_executes_chat”)) {boolean haveSubject = true;
boolean haveTopic = true;
boolean actorIsHostInChat = false;
boolean actorIsGuestInChat = false;
boolean chatExistsForSubjectAndTopic = false;
boolean actorRestartingCurrentChat = false;
// get chat topic chosen by hostchatTopic =
normalize((String)session.getAttribute(“chatTopic”)).trim();
// if no topic, prevent chat startif(chatTopic.length() < 1) {log(sessionId, “err”, “ERROR: SESSION HAS NO chatTopic,forwarding back to get it!”);
haveTopic = false;
}else if(chatTopic.equalsIgnoreCase(“NONE”)) {
Trang 14log(sessionId, “err”, “ERROR: SESSION HAS chatTopic=NONE,forwarding back to get it!”);
haveTopic = false;
}// get chat Subject chosen by hostchatSubject =
normalize((String)session.getAttribute(“chatSubject”)).trim();
// if no subject, prevent chat startif(chatSubject.length() < 1) {log(sessionId, “err”, “ERROR: SESSION HAS NO chatSubject,forwarding back to get it!”);
haveSubject = false;
}else if(chatSubject.equalsIgnoreCase(“NONE”)) {log(sessionId, “err”, “ERROR: SESSION HAS chatSubject=NONE,forwarding back to get it!”);
haveSubject = false;
}// Note: here we must synchronize two things:
// 1 the check to see subject+topic is taken// 2 the addition of new chats
// If we do not do that, two threads with same subject+topic// can go through here very close together, and this can happen:
// - The first checked subject+topic and is about to add chat, buthasn’t
// - The second checks it subject+topic and finds it clear to use
// - The first adds its chat with the same subject+topic
// - The second adds its chat with the same subject+topic// Problems! Using subject+topic to find chat again can only findone!
//
// The first thread (thus session) that enters the synchronizedblock
// gets a lock on bonForumStore object
// The synchronized block is then closed to all other threads
// Automatically blocked by Java, they must wait until they can// get the lock on the bonForumStore object before they can enter
// Thus the synchronization functions as a FIFO for the threads
//
// this almost works, needs something else synchronized?
//synchronized(bonForumStore.bonForumXML) {synchronized(bonForumStore) {
// If subject and topic are ok, see if that chat exists
// If so, check for two things:
// 1 Is it the current chat for session and actor is trying torestart it,
// 2 Is it a chat the actor is already in either as host or asguest
// (The third alternative is that it is a new chat to start.)if(haveSubject && haveTopic) {
// see if subject+topic combination exists as a chat
Trang 15String fakeChatItem = chatSubject + “_[“ + chatTopic + “]”;// replace all ‘.’ with ‘_’ which is separator in chatItem// ‘.’ is separator in pathNameHashtable
fakeChatItem = fakeChatItem.replace(‘.’, ‘_’);
String foundChatNodeKeyKey =getBonForumStore().getBonForumChatNodeKeyKey(fakeChatItem);
if((foundChatNodeKeyKey != null) &&
String newChatSubject =normalize((String)session.getAttribute(“newChatSubject”));
String newChatTopic =normalize((String)session.getAttribute(“newChatTopic”));
// see if it is the right one for subject andtopic
if(chatNodeKeyKey.equals(foundChatNodeKeyKey)) {actorRestartingCurrentChat = true;
}else {//CHAT GONE! WILL RESTART ONE FOR SUBJECT &TOPIC
chatExistsForSubjectAndTopic = false;actorRestartingCurrentChat = false;}
}}// see if actor is a host in chat found with requestedsubject and topic
String actorKeyValue =normalize((String)session.getAttribute(“hostKey”));
if(actorKeyValue.trim().length() > 0) {actorIsHostInChat =
getBonForumStore().isHostInChat(actorKeyValue, foundChatNodeKeyKey);
}if(!actorIsHostInChat) {// if not, see if actor is a guest in current chatactorKeyValue =
normalize((String)session.getAttribute(“guestKey”));
Trang 16if(actorKeyValue.trim().length() > 0) {actorIsGuestInChat =
getBonForumStore().isGuestInChat(actorKeyValue, foundChatNodeKeyKey);
}}}// If actor will rejoin existing chat,// route actor there using a bonForumCommand,// which determines next JSP destination
// when engine forwards request
boolean actorWillRejoinChat = false;
if(chatExistsForSubjectAndTopic) {// cannot start chat, it exists alreadyhaveTopic = false;
// Here later implement a user preference, using sessionattributes
// Choices can be offered for behavior of “visitor startschat” when chat exists
// 1 always warn user and ask again for new subjectand/or new topic
// 2 if actor was in it, always join with previousstatus, else warn and ask again
// 3 if actor was in it, always join as guest, else warnand ask again
// All these choices can be modified reactorRestartingCurrentChat value
// For now, we implement choice #2if(actorIsHostInChat) {
bonForumCommand = “host_executes_chat”;
actorWillRejoinChat = true;
else if(actorIsGuestInChat) {bonForumCommand = “guest_executes_chat”;
actorWillRejoinChat = true;
}else {// cannot start existing chat// set attribute for using onvisitor_starts_chat.jsp
// to trigger user message that chat exists:
Trang 17// are usually set when actor starts new chat.
//
if(actorWillRejoinChat) {// nodeNameHashtable key for the chat node key is neededfor:
// 1 adding messages to chat later
// 2 seeing if a chat is the current chatsession.setAttribute(“chatNodeKeyKey”,foundChatNodeKeyKey);
// host session doesn’t need this,// but if rejoining chat as guest, might?
session.setAttribute(“chatItem”, fakeChatItem);
// set the itemKey for this chat into a session attribute// item key is added as a message attribute later// it is needed for finding messages (temporarily)// and for any guest session to find chat
String foundChatItemKey =getBonForumStore().getBonForumChatItemNodeKey(fakeChatItem).toString();
session.setAttribute(“itemKey”, foundChatItemKey);}
}if(haveSubject && haveTopic) {// actor starts chat// Each actorNickname is unique in bonForum,// Only one host node is allowed per actorNicknameactorNickname =
normalize((String)session.getAttribute(“actorNickname”));
// Try getting key to a host node// for current actor’s nicknameNodeKey hostNicknameNodeKey =getBonForumStore().getActorNicknameNodeKey( actorNickname, “host” );
NodeKey hostNodeKey = null;
if(hostNicknameNodeKey != null) {BonNode hostNicknameNode =getBonForumStore().getBonForumXML().getBonNode( hostNicknameNodeKey);
hostNodeKey = hostNicknameNode.parentNodeKey;
}if(hostNodeKey == null) {// If a host node key does not exist,// then current actor is not yet a host,// so add a new host node,
// with actorNickname,// actorAge and// actorRating children,// to the “actors” root-child node// of bonForumXML
Trang 18hostNodeKey = (NodeKey)obj;
String creationTimeMillis = hostNodeKey.aKey;
String hostNodeKeyKey = sessionId + “_” +creationTimeMillis + “:host”;
// Make nodeNameHashtable key// for the hostNodeKeyKey// available to session
// It gives quick access to last host nodeKey for sessionsession.setAttribute( “hostNodeKeyKey”, hostNodeKeyKey );
// There are other similar lines that could be faster
// They are in host handling, but not in message or guesthandling
//bonForumStore.getBonForumXML().addChildNodeToNonRootNode(“actorNickname”, “”,content, hostNodeKey, “nodeNameHashtable”, sessionId);
}forestHashtableName = “bonForumXML”;
obj = bonForumStore.add( “bonAddElement”, hostNodeKeyKey,nameAndAttributes, content, forestHashtableName, “nodeNameHashtable”, sessionId );
}// Add a chat node to the “things”
// root-child node of bonForumXML,// with a chatModerated attribute,
Trang 19// and no text content.
chatModerated = normalize((String)session.getAttribute(
“chatModerated” ));
if (chatModerated.equalsIgnoreCase(“yes”)) {nameAndAttributes = “chat moderated=\”yes\””;
}else {nameAndAttributes = “chat moderated=\”no\””;
}content = “”;
forestHashtableName = “bonForumXML”;
obj = bonForumStore.add( “bonAddElement”, “things”,nameAndAttributes, content, forestHashtableName, “nodeNameHashtable”, sessionId );
NodeKey chatNodeKey = (NodeKey)obj;
// Add a hostKey to the new chat node,// its text content is the key to the host nodeString creationTimeMillis = chatNodeKey.aKey;
chatNodeKeyKey = sessionId + “_” + creationTimeMillis +
// Make the hostKey available to this session
// It is later used for these things:
// 1 finding out if an actor is a host in a chat// 2 branding messages with a host as sendersession.setAttribute(“hostKey”, content);
// Make nodeNameHashtable key// for the chat node key// available to session
// It is useful later for these things:
// 1 adding messages to chat// 2 finding the chat node// (to add nodes or attributes)// 3 determining if a chat is the current chatsession.setAttribute( “chatNodeKeyKey”, chatNodeKeyKey );// Add a “chatItem” child
// to the selected chat subject element
// That selected element is// the chat subject category// in bonForumXML
// The name of the new child is “sessionID_” +// the sessionId of
// the visitor starting the chat +// the time the chat node was created in millis
// The time suffix allows more than one chat// to exist per session
// Also add an attribute called chatTopic,// with the (escaped) chatTopic
Trang 20// input by the visitor.
// The sessionId (recoverable from// the name of the new child) can// be used later to quickly find the chat nodeKey
// That is useful for example// when a visitor joins a chat// Note: when adding the sessionId// element, its parent is found// using the pathNameHashtable
// The parent nodeKey is there// with a key which is its pathName// (and equal to chatSubject)nameAndAttributes = “sessionID_”;
NodeKey itemNodeKey = (NodeKey)obj;
// set itemKey to itemNodeKey as a stringString itemKey = itemNodeKey.toString();
// Add the key to the chatItem element (itemKey) // to the chat element as an attribute
// The itemKey connects a chat// to its subject, topic and messages!
String attributeName = “itemKey”;
String attributeValue = itemKey;
NodeKey nk = bonForumStore.addChatNodeAttribute(
chatNodeKeyKey, attributeName, attributeValue );
// Make the itemKey available to the sessionsession.setAttribute(“itemKey”, itemKey);
}if((!chatExistsForSubjectAndTopic) && (!haveSubject ||
!haveTopic)) {
// missing information, must return to get it// LATER: set attribute to trigger message to userbonForumCommand = “visitor_starts_chat”;
}} // end of synchronized block!
}// end of host_executes_chat processingelse if (bonForumCommand.equals(“guest_executes_chat”)) {// assume visitor will join chat
boolean haveChatItem = true;
boolean chatExistsForSubjectAndTopic = false;
// get chat Subject/Topic pair chosen by guestchatItem =
Trang 21if(chatItem.length() < 1) {haveChatItem = false;
log(sessionId, “err”,
“processRequest() ERROR: session has no chatItem, goingback for it!”);
}if(chatItem.equalsIgnoreCase(“NONE”)) {haveChatItem = false;
log(sessionId, “err”,
“processRequest() ERROR: session has chatItem=NONE, goingback for it!”);
}// For a guest to join a chat, we need to get sessionId of HOST// that started that chat! To find chat for that visitor tojoin,
// we will need the key to its nodeKey in the nodeNameHashtable.// We call that key the chatNodeKeyKey, and can use it to add// elements, etc directly to the chat Note that we CANNOT just// make a new “<sessionId>_<creationTimeMillis>:chat” keystring,
// because the chat was created by the host’s sessionId and we// have only the guest’s sessionId now! So we get the right// chatNodeKeyKey for the chat, which is
// <hostSessionId>_<creationTimeMillis>:chat// directly from the nodeName of the chosen chatItem elementwhich
// was saved when the chat was started This way there is noneed
// to iterate the chats looking for the one with the rightitemKey
// Instead, we will be able to use:
// chatNodeKeyKey =session.getAttribute(“chatNodeKeyKey”);
// that more meaningful chat selection list
// NOTE: we must disallow ‘>[‘ in subject node names for this towork!
// NOTE: For now, separator in incoming chat subject path iswired as ‘_’
// e.g Animals_Fish_Piranha_[first aid for fish breeders]// NOTE: the creationTimeMillis portion of the nodeNameHashtablekeys
// allows hosts and guests to be in more than one chat,// and allows actors to host and guest multiple chats
// NOTE: there is also an option to leave out the nodeKey.aKey
Trang 22// only one host node per session, etc.
// As in the case of “visitor starts chat”
// (see the note before synchronize block above),// we synchronize here two things:
// 1 the check for existence of chat for subject+topic(chatItem)
// 2 the joining of a chat by visitor// - We assume that the chat for chatItem exists, since it waspicked
// from a list generated by the webapp from existing chats
// - When chat deletion is implemented,// (and even now, because chats expire with their session),// we need to guard against adding part of the elements, etc
// required for joining a chat, and then not being able tocomplete all
// the additions required If synchronization turns out toaffect
// performance too much, we could make a background task toclean up
// such debris?
// almost works, needs something else synchronized?
//synchronized(bonForumStore.bonForumXML) {synchronized(bonForumStore) {
if(haveChatItem) {boolean actorIsHostInChat = false;
boolean actorIsGuestInChat = false;
boolean actorWillRejoinChat = false;
// See if chatItem (i.e., subject+topic) exists already as
a chat:
chatNodeKeyKey =getBonForumStore().getBonForumChatNodeKeyKey(chatItem);
if((chatNodeKeyKey != null) && (chatNodeKeyKey.length() >
0)) {
// Being able to find a chatNodeKeyKey from chatItem// means that a chat exists with chosen subject andtopic
Trang 23getBonForumStore().isHostInChat(actorKeyValue, chatNodeKeyKey);
}if(!actorIsHostInChat) {// if not, see if actor is a guest in chatfound
actorKeyValue =normalize((String)session.getAttribute(“guestKey”));
if(actorKeyValue.trim().length() > 0) {actorIsGuestInChat =
getBonForumStore().isGuestInChat(actorKeyValue, chatNodeKeyKey);
}}}// see if visitor will rejoin chat// because already a host or guest,// or will join chat becoming a new guestif(chatExistsForSubjectAndTopic) {// set the chatNodeKeyKey for this chat into asession attribute
session.setAttribute(“chatNodeKeyKey”,chatNodeKeyKey);
// if visitor already in chat as host or guest, willre-join it
if(actorIsHostInChat) {bonForumCommand = “host_executes_chat”;actorWillRejoinChat = true;
haveChatItem = false;
}else if(actorIsGuestInChat) {//bonForumCommand = “guest_executes_chat”;actorWillRejoinChat = true;
haveChatItem = false;
}else {// visitor not in chat yet, so will join asguest
//bonForumCommand = “guest_executes_chat”;}
}// set session attributes for message handlingif(actorWillRejoinChat) {
// not needed, hasn’t changed?
// session.setAttribute(“chatItem”, chatItem);// set the itemKey for this chat into a sessionattribute
// it is needed for finding messages (temporarily)// and for any guest session to find it
String foundChatItemKey =getBonForumStore().getBonForumChatItemNodeKey(chatItem).toString();
session.setAttribute(“itemKey”, foundChatItemKey);}
Trang 24}// check chat node OK before doing anything elsechatNode =
bonForumStore.getBonForumChatNode(chatNodeKeyKey);
if(chatNode == null) {haveChatItem = false;
bonForumCommand = “forum_error”;
request.setAttribute(“serviceStatus”,
“ForwardToErrorPage”);
log(sessionId, “err”,
“ERROR! No chatNode in guest_executes_chat handler!
Forwarding To Error Page!”);
}}// actor joins chatif(haveChatItem) {// actorNickname is unique in bonForum,// Allow only one guest node per actorNickname// Get the guest nickname from sessionactorNickname =
normalize((String)session.getAttribute(“actorNickname”));
// Get guest nickname keyNodeKey guestNicknameNodeKey =getBonForumStore().getActorNicknameNodeKey(actorNickname, “guest”);
NodeKey guestNodeKey = null;
// If got key, get guest nickname node,// use its parent key to get guest node keyif(guestNicknameNodeKey != null) {
BonNode guestNicknameNode =getBonForumStore().getBonForumXML().getBonNode(guestNicknameNodeKey);
guestNodeKey = guestNicknameNode.parentNodeKey;
}// If guest node key does not exist,// neither does guest, so add guest node,// with its nickname, age and rating children// to the “actors” rootchild node of database
if(guestNodeKey == null) {//add guest node to actorsnameAndAttributes = “guest”;
// It is used also in the nodeKeyHashtable keyvalues
String creationTimeMillis = guestNodeKey.aKey;
String guestNodeKeyKey = sessionId + “_” +
Trang 25creationTimeMillis + “:guest”;
// Make nodeNameHashtable key// for the guestNodeKey// available to session
// It gives quick access to last guest nodeKey forsession
session.setAttribute( “guestNodeKeyKey”,guestNodeKeyKey );
// add actorNickname to guestnameAndAttributes = “actorNickname”;
content =normalize((String)session.getAttribute(“actorNickname”));
forestHashtableName = “bonForumXML”;
obj = bonForumStore.add(“bonAddElement”,guestNodeKeyKey, nameAndAttributes, content, forestHashtableName,
forestHashtableName = “bonForumXML”;
obj = bonForumStore.add(“bonAddElement”,guestNodeKeyKey, nameAndAttributes, content, forestHashtableName,
“nodeNameHashtable”, sessionId);
// add actorRating to guestnameAndAttributes = “actorRating”;
content =normalize((String)session.getAttribute(“actorRating”));
if(content.length() < 1) {content = “5”;
}forestHashtableName = “bonForumXML”;
obj = bonForumStore.add(“bonAddElement”,guestNodeKeyKey, nameAndAttributes, content, forestHashtableName,
“nodeNameHashtable”, sessionId);
}// add guestKey to chat, that is how guest joins chat.nameAndAttributes = “guestKey”;
content = guestNodeKey.toString();
forestHashtableName = “bonForumXML”;
//chatNodeKeyKey =normalize((String)session.getAttribute(“chatNodeKeyKey”));
obj = bonForumStore.add(“bonAddElement”, chatNodeKeyKey,nameAndAttributes, content, forestHashtableName, “nodeNameHashtable”, sessionId);
// set the guestKey for this chat into a session attributesession.setAttribute(“guestKey”, guestNodeKey.toString());// set the itemKey for this chat into a session attributefor the guest’s session
String chatItemKey =