Commit 3f908f1a authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gaston

1) Fixed locking logic that may freeze the server. JM-114

2) Minor code refactoring.


git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@851 b35dd754-fafc-0310-a699-88a17e54d16e
parent 74f73460
...@@ -466,29 +466,6 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -466,29 +466,6 @@ public class MUCRoomImpl implements MUCRoom {
joinRole = joinRole =
new MUCRoleImpl(server, this, nickname, role, affiliation, (MUCUserImpl) user, new MUCRoleImpl(server, this, nickname, role, affiliation, (MUCUserImpl) user,
presence, router); presence, router);
// Send presence of existing occupants to new occupant
for (MUCRole occupantsRole : occupants.values()) {
Presence occupantsPresence = occupantsRole.getPresence().createCopy();
// Skip to the next occupant if we cannot send presence of this occupant
if (hasToCheckRoleToBroadcastPresence()) {
Element frag = occupantsPresence.getChildElement("x",
"http://jabber.org/protocol/muc#user");
// Check if we can broadcast the presence for this role
if (!canBroadcastPresence(frag.element("item").attributeValue("role"))) {
continue;
}
}
// Don't include the occupant's JID if the room is semi-anon and the new occupant
// is not a moderator
if (!canAnyoneDiscoverJID() && MUCRole.MODERATOR != joinRole.getRole()) {
Element frag = occupantsPresence.getChildElement(
"x",
"http://jabber.org/protocol/muc#user");
frag.element("item").addAttribute("jid", null);
}
joinRole.send(occupantsPresence);
}
// Add the new user as an occupant of this room // Add the new user as an occupant of this room
occupants.put(nickname.toLowerCase(), joinRole); occupants.put(nickname.toLowerCase(), joinRole);
// Update the tables of occupants based on the bare and full JID // Update the tables of occupants based on the bare and full JID
...@@ -503,7 +480,8 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -503,7 +480,8 @@ public class MUCRoomImpl implements MUCRoom {
finally { finally {
lock.writeLock().unlock(); lock.writeLock().unlock();
} }
if (joinRole != null) { // Send presence of existing occupants to new occupant
sendInitialPresences(joinRole);
// It is assumed that the room is new based on the fact that it's locked and // It is assumed that the room is new based on the fact that it's locked and
// that it was locked when it was created. // that it was locked when it was created.
boolean isRoomNew = isLocked() && creationDate.getTime() == lockedTime; boolean isRoomNew = isLocked() && creationDate.getTime() == lockedTime;
...@@ -527,8 +505,7 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -527,8 +505,7 @@ public class MUCRoomImpl implements MUCRoom {
message.setType(Message.Type.groupchat); message.setType(Message.Type.groupchat);
message.setBody(LocaleUtils.getLocalizedString("muc.new")); message.setBody(LocaleUtils.getLocalizedString("muc.new"));
message.setFrom(role.getRoleAddress()); message.setFrom(role.getRoleAddress());
message.setTo(user.getAddress()); joinRole.send(message);
router.route(message);
} }
else if (isLocked()) { else if (isLocked()) {
// Warn the owner that the room is locked but it's not new // Warn the owner that the room is locked but it's not new
...@@ -536,8 +513,7 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -536,8 +513,7 @@ public class MUCRoomImpl implements MUCRoom {
message.setType(Message.Type.groupchat); message.setType(Message.Type.groupchat);
message.setBody(LocaleUtils.getLocalizedString("muc.locked")); message.setBody(LocaleUtils.getLocalizedString("muc.locked"));
message.setFrom(role.getRoleAddress()); message.setFrom(role.getRoleAddress());
message.setTo(user.getAddress()); joinRole.send(message);
router.route(message);
} }
else if (canAnyoneDiscoverJID()) { else if (canAnyoneDiscoverJID()) {
// Warn the new occupant that the room is non-anonymous (i.e. his JID will be // Warn the new occupant that the room is non-anonymous (i.e. his JID will be
...@@ -546,10 +522,9 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -546,10 +522,9 @@ public class MUCRoomImpl implements MUCRoom {
message.setType(Message.Type.groupchat); message.setType(Message.Type.groupchat);
message.setBody(LocaleUtils.getLocalizedString("muc.warnnonanonymous")); message.setBody(LocaleUtils.getLocalizedString("muc.warnnonanonymous"));
message.setFrom(role.getRoleAddress()); message.setFrom(role.getRoleAddress());
message.setTo(user.getAddress());
Element frag = message.addChildElement("x", "http://jabber.org/protocol/muc#user"); Element frag = message.addChildElement("x", "http://jabber.org/protocol/muc#user");
frag.addElement("status").addAttribute("code", "100"); frag.addElement("status").addAttribute("code", "100");
router.route(message); joinRole.send(message);
} }
if (historyRequest == null) { if (historyRequest == null) {
Iterator history = roomHistory.getMessageHistory(); Iterator history = roomHistory.getMessageHistory();
...@@ -562,10 +537,40 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -562,10 +537,40 @@ public class MUCRoomImpl implements MUCRoom {
} }
// Update the date when the last occupant left the room // Update the date when the last occupant left the room
setEmptyDate(null); setEmptyDate(null);
}
return joinRole; return joinRole;
} }
/**
* Sends presence of existing occupants to new occupant.
*
* @param joinRole the role of the new occupant in the room.
*/
private void sendInitialPresences(MUCRoleImpl joinRole) {
for (MUCRole occupant : occupants.values()) {
if (occupant == joinRole) {
continue;
}
Presence occupantPresence = occupant.getPresence().createCopy();
// Skip to the next occupant if we cannot send presence of this occupant
if (hasToCheckRoleToBroadcastPresence()) {
Element frag = occupantPresence.getChildElement("x",
"http://jabber.org/protocol/muc#user");
// Check if we can broadcast the presence for this role
if (!canBroadcastPresence(frag.element("item").attributeValue("role"))) {
continue;
}
}
// Don't include the occupant's JID if the room is semi-anon and the new occupant
// is not a moderator
if (!canAnyoneDiscoverJID() && MUCRole.MODERATOR != joinRole.getRole()) {
Element frag = occupantPresence.getChildElement("x",
"http://jabber.org/protocol/muc#user");
frag.element("item").addAttribute("jid", null);
}
joinRole.send(occupantPresence);
}
}
public void leaveRoom(String nickname) throws UserNotFoundException { public void leaveRoom(String nickname) throws UserNotFoundException {
MUCRole leaveRole = null; MUCRole leaveRole = null;
lock.writeLock().lock(); lock.writeLock().lock();
...@@ -588,7 +593,6 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -588,7 +593,6 @@ public class MUCRoomImpl implements MUCRoom {
// not persistent // not persistent
if (occupants.isEmpty() && !isPersistent()) { if (occupants.isEmpty() && !isPersistent()) {
endTime = System.currentTimeMillis(); endTime = System.currentTimeMillis();
server.removeChatRoom(name); server.removeChatRoom(name);
} }
if (occupants.isEmpty()) { if (occupants.isEmpty()) {
...@@ -600,28 +604,30 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -600,28 +604,30 @@ public class MUCRoomImpl implements MUCRoom {
lock.writeLock().unlock(); lock.writeLock().unlock();
} }
if (leaveRole != null) {
try { try {
// Inform the rest of the room occupants that the user has left the room
Presence presence = leaveRole.getPresence().createCopy(); Presence presence = leaveRole.getPresence().createCopy();
// Switch the presence to OFFLINE
presence.setType(Presence.Type.unavailable); presence.setType(Presence.Type.unavailable);
presence.setStatus(null); presence.setStatus(null);
broadcastPresence(presence); broadcastPresence(presence);
leaveRole.kick();
} }
catch (Exception e) { catch (Exception e) {
Log.error(e); Log.error(e);
} }
} }
}
/** /**
* @param leaveRole * Removes the role of the occupant from all the internal occupants collections. The role will
* also be removed from the user's roles.
*
* @param leaveRole the role to remove.
*/ */
private void removeOccupantRole(MUCRole leaveRole) { private void removeOccupantRole(MUCRole leaveRole) {
occupants.remove(leaveRole.getNickname().toLowerCase()); occupants.remove(leaveRole.getNickname().toLowerCase());
MUCUser user = leaveRole.getChatUser(); MUCUser user = leaveRole.getChatUser();
// Notify the user that he/she is no longer in the room
user.removeRole(getName());
// Update the tables of occupants based on the bare and full JID // Update the tables of occupants based on the bare and full JID
List list = occupantsByBareJID.get(user.getAddress().toBareJID()); List list = occupantsByBareJID.get(user.getAddress().toBareJID());
if (list != null) { if (list != null) {
...@@ -635,6 +641,7 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -635,6 +641,7 @@ public class MUCRoomImpl implements MUCRoom {
public void destroyRoom(String alternateJID, String reason) { public void destroyRoom(String alternateJID, String reason) {
MUCRole leaveRole = null; MUCRole leaveRole = null;
Collection<MUCRole> removedRoles = new ArrayList<MUCRole>();
lock.writeLock().lock(); lock.writeLock().lock();
try { try {
// Remove each occupant // Remove each occupant
...@@ -642,11 +649,27 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -642,11 +649,27 @@ public class MUCRoomImpl implements MUCRoom {
leaveRole = occupants.remove(nickname); leaveRole = occupants.remove(nickname);
if (leaveRole != null) { if (leaveRole != null) {
// Add the removed occupant to the list of removed occupants. We are keeping a
// list of removed occupants to process later outside of the lock.
removedRoles.add(leaveRole);
removeOccupantRole(leaveRole);
}
}
endTime = System.currentTimeMillis();
// Removes the room from the list of rooms hosted in the server
server.removeChatRoom(name);
// Set that the room has been destroyed
isDestroyed = true;
}
finally {
lock.writeLock().unlock();
}
// Send an unavailable presence to each removed occupant
for (MUCRole removedRole : removedRoles) {
try { try {
// Send a presence stanza of type "unavailable" to the occupant // Send a presence stanza of type "unavailable" to the occupant
Presence presence = createPresence(Presence.Type.unavailable); Presence presence = createPresence(Presence.Type.unavailable);
presence.setFrom(leaveRole.getRoleAddress()); presence.setFrom(removedRole.getRoleAddress());
presence.setTo(leaveRole.getChatUser().getAddress());
// A fragment containing the x-extension for room destruction. // A fragment containing the x-extension for room destruction.
Element fragment = presence.addChildElement("x", Element fragment = presence.addChildElement("x",
...@@ -664,25 +687,14 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -664,25 +687,14 @@ public class MUCRoomImpl implements MUCRoom {
} }
destroy.addElement("reason").setText(reason); destroy.addElement("reason").setText(reason);
} }
removedRole.send(presence);
router.route(presence);
leaveRole.kick();
} }
catch (Exception e) { catch (Exception e) {
Log.error(e); Log.error(e);
} }
} }
} // Remove the room from the DB if the room was persistent
endTime = System.currentTimeMillis();
MUCPersistenceManager.deleteFromDB(this); MUCPersistenceManager.deleteFromDB(this);
server.removeChatRoom(name);
// Set that the room has been destroyed
isDestroyed = true;
}
finally {
lock.writeLock().unlock();
}
} }
public Presence createPresence(Presence.Type presenceType) throws UnauthorizedException { public Presence createPresence(Presence.Type presenceType) throws UnauthorizedException {
...@@ -716,8 +728,7 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -716,8 +728,7 @@ public class MUCRoomImpl implements MUCRoom {
MUCRole occupant = occupants.get(resource.toLowerCase()); MUCRole occupant = occupants.get(resource.toLowerCase());
if (occupant != null) { if (occupant != null) {
message.setFrom(senderRole.getRoleAddress()); message.setFrom(senderRole.getRoleAddress());
message.setTo(occupant.getChatUser().getAddress()); occupant.send(message);
router.route(message);
} }
else { else {
throw new NotFoundException(); throw new NotFoundException();
...@@ -742,14 +753,20 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -742,14 +753,20 @@ public class MUCRoomImpl implements MUCRoom {
router.route(packet); router.route(packet);
} }
/**
* Broadcasts the specified presence to all room occupants. If the presence belongs to a
* user whose role cannot be broadcast then the presence will only be sent to the presence's
* user. On the other hand, the JID of the user that sent the presence won't be included if the
* room is semi-anon and the target occupant is not a moderator.
*
* @param presence the presence to broadcast.
*/
private void broadcastPresence(Presence presence) { private void broadcastPresence(Presence presence) {
if (presence == null) { if (presence == null) {
return; return;
} }
Element frag = null; Element frag = null;
String jid = null; String jid = null;
if (hasToCheckRoleToBroadcastPresence()) { if (hasToCheckRoleToBroadcastPresence()) {
frag = presence.getChildElement("x", "http://jabber.org/protocol/muc#user"); frag = presence.getChildElement("x", "http://jabber.org/protocol/muc#user");
// Check if we can broadcast the presence for this role // Check if we can broadcast the presence for this role
...@@ -757,8 +774,7 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -757,8 +774,7 @@ public class MUCRoomImpl implements MUCRoom {
// Just send the presence to the sender of the presence // Just send the presence to the sender of the presence
try { try {
MUCRole occupant = getOccupant(presence.getFrom().getResource()); MUCRole occupant = getOccupant(presence.getFrom().getResource());
presence.setTo(occupant.getChatUser().getAddress()); occupant.send(presence);
router.route(presence);
} }
catch (UserNotFoundException e) { catch (UserNotFoundException e) {
// Do nothing // Do nothing
...@@ -776,7 +792,6 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -776,7 +792,6 @@ public class MUCRoomImpl implements MUCRoom {
jid = frag.element("item").attributeValue("jid"); jid = frag.element("item").attributeValue("jid");
} }
for (MUCRole occupant : occupants.values()) { for (MUCRole occupant : occupants.values()) {
presence.setTo(occupant.getChatUser().getAddress());
// Don't include the occupant's JID if the room is semi-anon and the new occupant // Don't include the occupant's JID if the room is semi-anon and the new occupant
// is not a moderator // is not a moderator
if (!canAnyoneDiscoverJID()) { if (!canAnyoneDiscoverJID()) {
...@@ -787,16 +802,13 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -787,16 +802,13 @@ public class MUCRoomImpl implements MUCRoom {
frag.element("item").addAttribute("jid", null); frag.element("item").addAttribute("jid", null);
} }
} }
router.route(presence); occupant.send(presence);
} }
} }
private void broadcast(Message message) { private void broadcast(Message message) {
lock.readLock().lock();
try {
for (MUCRole occupant : occupants.values()) { for (MUCRole occupant : occupants.values()) {
message.setTo(occupant.getChatUser().getAddress()); occupant.send(message);
router.route(message);
} }
if (isLogEnabled()) { if (isLogEnabled()) {
MUCRole senderRole = null; MUCRole senderRole = null;
...@@ -816,10 +828,6 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -816,10 +828,6 @@ public class MUCRoomImpl implements MUCRoom {
server.logConversation(this, message, senderAddress); server.logConversation(this, message, senderAddress);
} }
} }
finally {
lock.readLock().unlock();
}
}
/** /**
* An empty role that represents the room itself in the chatroom. Chatrooms need to be able to * An empty role that represents the room itself in the chatroom. Chatrooms need to be able to
...@@ -870,9 +878,6 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -870,9 +878,6 @@ public class MUCRoomImpl implements MUCRoom {
return null; return null;
} }
public void kick() {
}
public MUCUser getChatUser() { public MUCUser getChatUser() {
return null; return null;
} }
...@@ -1475,7 +1480,6 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -1475,7 +1480,6 @@ public class MUCRoomImpl implements MUCRoom {
kickedRole.send(kickPresence); kickedRole.send(kickPresence);
// Remove the occupant from the room's occupants lists // Remove the occupant from the room's occupants lists
removeOccupantRole(kickedRole); removeOccupantRole(kickedRole);
kickedRole.kick();
} }
} }
...@@ -1646,8 +1650,7 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -1646,8 +1650,7 @@ public class MUCRoomImpl implements MUCRoom {
message.setType(Message.Type.groupchat); message.setType(Message.Type.groupchat);
message.setBody(LocaleUtils.getLocalizedString("muc.locked")); message.setBody(LocaleUtils.getLocalizedString("muc.locked"));
message.setFrom(getRole().getRoleAddress()); message.setFrom(getRole().getRoleAddress());
message.setTo(senderRole.getChatUser().getAddress()); senderRole.send(message);
router.route(message);
} }
} }
...@@ -1666,8 +1669,7 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -1666,8 +1669,7 @@ public class MUCRoomImpl implements MUCRoom {
message.setType(Message.Type.groupchat); message.setType(Message.Type.groupchat);
message.setBody(LocaleUtils.getLocalizedString("muc.unlocked")); message.setBody(LocaleUtils.getLocalizedString("muc.unlocked"));
message.setFrom(getRole().getRoleAddress()); message.setFrom(getRole().getRoleAddress());
message.setTo(senderRole.getChatUser().getAddress()); senderRole.send(message);
router.route(message);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment