2.4.1 View
-
SessionView
chatroom.view.xml:
<view name="UserInfo" lifecycle="session"> <bind name="name" table="userinfo"> <ref name="nickname" /> </bind> <variable name="recvedmessage" type="ChatMessage" /> <variable name="sendedmessage" type="ChatMessage" /> <variable name="lasterror" type="int" /> </view>
-
<variable name="recvedmessage" type="ChatMessage" />
<variable name="sendedmessage" type="ChatMessage" >
These two fields are implemented as the private chat.
The user sends the chat type Message to the current ChatRoom. The getMessageTo() here returns the acceptor user id, toid!=-1.
chatroom.js:
function sendMessage() { var sendmsg = document.getElementById('sendmsg'); var msg = sendmsg.value.trim(); if (msg.length > 0) context.send(currentChatView, "message=" + getMessageTo() + "," + msg); sendmsg.value = ""; }
The server ChatRoom receives the Message and processes it.
ChatRoom.java:
protected void onMessage(String message, long sessionid) { int index = message.indexOf('='); if (-1 == index) return; final String cmd = message.substring(0, index).trim(); final String params = message.substring(index + 1); switch (cmd) { case "message": { .................................. if (-1L == toid) { .................................. } else { UserInfo.getInstance(toid).setRecvedmessage(new ChatMessage(msg, sessionid)); UserInfo.getInstance(sessionid).setSendedmessage(new ChatMessage(msg, toid)); RoomInfo.increment_privateMessages_mapkey(roomid); } return; } .................................. }
UserInfo.getInstance(toid).setRecvedmessage notify the message and the sender id to the receiver.
UserInfo.getInstance(sessionid).setSendedmessage notify the message and the receiver id to the sender
chatroom.js:
ctx.register(v100.chatviews.UserInfo, "recvedmessage", function(e) { onReceiveMessage(e.value); }); ctx.register(v100.chatviews.UserInfo, "sendedmessage", function(e) { onSendedMessage(e.value); });
receiver page displays xxx to you : msg
sender page displays you to xxx : msg
-
<bind name="name" table="userinfo">
This field is used to set the user nickname.
UserInfo.java:
private UserInfo(SessionView.CreateParameter param) { super(param); // bind here bindUserinfo(sessionid); long sessionid = param.getSessionId(); Procedure.execute(ProcedureHelper.nameProcedure("UserInfo init nickname", () -> { xbean.UserInfo userinfo = table.Userinfo.insert(sessionid); if (userinfo != null) userinfo.setNickname(""); return true; })); logintime = System.currentTimeMillis(); checkUpdateHallInfos(); }
The user enter and the UserInfo is automatically created.
<bind name="name" table="userinfo"> <ref name="nickname" /> </bind>
chatroom.zdb.xml:
<xbean name="UserInfo"> <variable name="nickname" type="string" /> </xbean> <table name="userinfo" key="long" value="UserInfo" />
The bindUserinfo creates the binding between the name and the xbean field nickname of the userinfo table. The key of the userinfo table corresponds to the sessionid here, which means that once the xbean field nickname of the userinfo table changes, this change is synchronized to the client immediately. When bindUserinfo, if the record of the sessionid exists, this record is synchronized to the client as the initial value; if not exist, no information is sent which represents as not exist, in this condition, the relationship created by the bind still exists and the change in the future will be synchronized to the client.
The storage procedure initiates the nickname. If insert operation successes which means that the record does not exist, the nickname is set as empty string in this condition, so that the nickname will be initiated when the client detects the empty string. If insert operation fails which means that the record exists, the previous bindUserinfo synchronizes the record to the client in this condition.
In general, this is a typical bind usage. First bind, then initiate the corresponding record. Either the record exists, and the record value is sent out. Or the storage procedure initiates the value which causes the change, this initial value is sent out because of the bind relationship.
chatroom.js:
ctx.register(v100.chatviews.UserInfo, "name", function(e) { if (e.value.nickname == "") doRename(); else showNickname(); });
The client detects that the value of the name changes. If the value is empty string, the client calls the doRename() to initiate the nickname.
chatroom.js:
function doRename() { var v100 = context[100]; var name = prompt("input name", v100.chatviews.UserInfo.name.nickname); if (name != null && name.length != 0 && name != v100.chatviews.UserInfo.name.nickname) context.send(v100.chatviews.UserInfo, "nickname=" + name); }
This method is used to initiate the name. After obtaining the name, Message is sent to the UserInfo to notify the new name.
UserInfo.java:
protected void onMessage(String message, long sessionid) { final int index = message.indexOf('='); if (-1 == index) return; checkUpdateHallInfos(); final String cmd = message.substring(0, index).trim(); final String param = message.substring(index + 1).trim(); switch (cmd) { case "nickname": Procedure.execute(ProcedureHelper.nameProcedure("onMessage nickname", () -> { xbean.UserInfo info = table.Userinfo.update(sessionid); if (param.equals(info.getNickname())) UserInfo.this._setLasterror(ErrorCodes.EC_NAME_UNMODIFIED); return false; } if (!GlobalId.create("chatserver", param)) { UserInfo.this._setLasterror(ErrorCodes.EC_NAME_EXISTING); return false; } info.setNickname(param); SysInfo.increment_nickNameChange(); return true; })); return; .............................................. } }
The first if statement here judges whether the nickname changes. It is used to avoid that the client forgets to do the detection.
The second if statement registers the name from the GlobalId. If the false returns which means the duplicated name, the duplicated name error is reported, here ErrorCodes.EC_NAME_EXISTING = 2. The create, delete, exists method of the GlobalId must be used in the transaction environment --- the storage procedure. These methods are used in the transaction, so that the allocated name will be automatically released when the transaction rollback.
It is noted that the two error reports use the _setLasterror. Once the field set method of the underscore version of the View is called, the View is updated immediately and synchronizes the data to the client. The none-underscore version will really update the View until the transaction is submitted in the transaction environment, and ignore the update when transaction rollback. Using underscore version in the error report is the normal usage.
The setNickname modifies the current user's nickname in the userinfo table. After modification successes, this modified value will be automatically synchronized to the client because of the bind.
If modification successes, the client:
chatroom.js:
ctx.register(v100.chatviews.UserInfo, "name", function(e) { if (e.value.nickname == "") doRename(); else showNickname(); });
The updated nickname is displayed here.
If the duplicated name is detected:
chatroom.js:
ctx.register(v100.chatviews.UserInfo, "lasterror", function(e) { if (e.value == 2) doRename(); else showErrorFrame(e.value); });
The error code of duplicated name is 2. Calling rename again is required here. The other errors display an error page.
-
<variable name="lasterror" type="int" />
Report the error to the user, and the definition of the error code is:
chatroom.view.xml:
<bean name="ErrorCodes"> <enum name="EC_SUCCEED" value="0" /> <enum name="EC_NAME_UNMODIFIED" value="1" /> <enum name="EC_NAME_EXISTING" value="2" /> <enum name="EC_BAD_ROOM_ID" value="11" /> <enum name="EC_BAD_ARGS" value="12" /> <enum name="EC_BAD_HALL_NAME" value="13" /> <enum name="EC_BAD_ROOM_NAME" value="14" /> </bean>
-