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,67 +480,95 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -503,67 +480,95 @@ public class MUCRoomImpl implements MUCRoom {
finally { finally {
lock.writeLock().unlock(); lock.writeLock().unlock();
} }
if (joinRole != null) { // Send presence of existing occupants to new occupant
// It is assumed that the room is new based on the fact that it's locked and sendInitialPresences(joinRole);
// that it was locked when it was created. // It is assumed that the room is new based on the fact that it's locked and
boolean isRoomNew = isLocked() && creationDate.getTime() == lockedTime; // that it was locked when it was created.
try { boolean isRoomNew = isLocked() && creationDate.getTime() == lockedTime;
// Send the presence of this new occupant to existing occupants try {
Presence joinPresence = joinRole.getPresence().createCopy(); // Send the presence of this new occupant to existing occupants
if (isRoomNew) { Presence joinPresence = joinRole.getPresence().createCopy();
Element frag = joinPresence.getChildElement(
"x", "http://jabber.org/protocol/muc#user");
frag.addElement("status").addAttribute("code", "201");
}
broadcastPresence(joinPresence);
}
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
// If the room has just been created send the "room locked until configuration is
// confirmed" message
if (isRoomNew) { if (isRoomNew) {
Message message = new Message(); Element frag = joinPresence.getChildElement(
message.setType(Message.Type.groupchat); "x", "http://jabber.org/protocol/muc#user");
message.setBody(LocaleUtils.getLocalizedString("muc.new")); frag.addElement("status").addAttribute("code", "201");
message.setFrom(role.getRoleAddress());
message.setTo(user.getAddress());
router.route(message);
} }
else if (isLocked()) { broadcastPresence(joinPresence);
// Warn the owner that the room is locked but it's not new }
Message message = new Message(); catch (Exception e) {
message.setType(Message.Type.groupchat); Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
message.setBody(LocaleUtils.getLocalizedString("muc.locked")); }
message.setFrom(role.getRoleAddress()); // If the room has just been created send the "room locked until configuration is
message.setTo(user.getAddress()); // confirmed" message
router.route(message); if (isRoomNew) {
Message message = new Message();
message.setType(Message.Type.groupchat);
message.setBody(LocaleUtils.getLocalizedString("muc.new"));
message.setFrom(role.getRoleAddress());
joinRole.send(message);
}
else if (isLocked()) {
// Warn the owner that the room is locked but it's not new
Message message = new Message();
message.setType(Message.Type.groupchat);
message.setBody(LocaleUtils.getLocalizedString("muc.locked"));
message.setFrom(role.getRoleAddress());
joinRole.send(message);
}
else if (canAnyoneDiscoverJID()) {
// Warn the new occupant that the room is non-anonymous (i.e. his JID will be
// public)
Message message = new Message();
message.setType(Message.Type.groupchat);
message.setBody(LocaleUtils.getLocalizedString("muc.warnnonanonymous"));
message.setFrom(role.getRoleAddress());
Element frag = message.addChildElement("x", "http://jabber.org/protocol/muc#user");
frag.addElement("status").addAttribute("code", "100");
joinRole.send(message);
}
if (historyRequest == null) {
Iterator history = roomHistory.getMessageHistory();
while (history.hasNext()) {
joinRole.send((Message) history.next());
} }
else if (canAnyoneDiscoverJID()) { }
// Warn the new occupant that the room is non-anonymous (i.e. his JID will be else {
// public) historyRequest.sendHistory(joinRole, roomHistory);
Message message = new Message(); }
message.setType(Message.Type.groupchat); // Update the date when the last occupant left the room
message.setBody(LocaleUtils.getLocalizedString("muc.warnnonanonymous")); setEmptyDate(null);
message.setFrom(role.getRoleAddress()); return joinRole;
message.setTo(user.getAddress()); }
Element frag = message.addChildElement("x", "http://jabber.org/protocol/muc#user");
frag.addElement("status").addAttribute("code", "100"); /**
router.route(message); * 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;
} }
if (historyRequest == null) { Presence occupantPresence = occupant.getPresence().createCopy();
Iterator history = roomHistory.getMessageHistory(); // Skip to the next occupant if we cannot send presence of this occupant
while (history.hasNext()) { if (hasToCheckRoleToBroadcastPresence()) {
joinRole.send((Message) history.next()); 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;
} }
} }
else { // Don't include the occupant's JID if the room is semi-anon and the new occupant
historyRequest.sendHistory(joinRole, roomHistory); // 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);
} }
// Update the date when the last occupant left the room joinRole.send(occupantPresence);
setEmptyDate(null);
} }
return joinRole;
} }
public void leaveRoom(String nickname) throws UserNotFoundException { public void leaveRoom(String nickname) throws UserNotFoundException {
...@@ -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) {
} Log.error(e);
catch (Exception 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,40 +649,14 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -642,40 +649,14 @@ public class MUCRoomImpl implements MUCRoom {
leaveRole = occupants.remove(nickname); leaveRole = occupants.remove(nickname);
if (leaveRole != null) { if (leaveRole != null) {
try { // Add the removed occupant to the list of removed occupants. We are keeping a
// Send a presence stanza of type "unavailable" to the occupant // list of removed occupants to process later outside of the lock.
Presence presence = createPresence(Presence.Type.unavailable); removedRoles.add(leaveRole);
presence.setFrom(leaveRole.getRoleAddress()); removeOccupantRole(leaveRole);
presence.setTo(leaveRole.getChatUser().getAddress());
// A fragment containing the x-extension for room destruction.
Element fragment = presence.addChildElement("x",
"http://jabber.org/protocol/muc#user");
Element item = fragment.addElement("item");
item.addAttribute("affiliation", "none");
item.addAttribute("role", "none");
if (alternateJID != null && alternateJID.length() > 0) {
fragment.addElement("destroy").addAttribute("jid", alternateJID);
}
if (reason != null && reason.length() > 0) {
Element destroy = fragment.element("destroy");
if (destroy == null) {
destroy = fragment.addElement("destroy");
}
destroy.addElement("reason").setText(reason);
}
router.route(presence);
leaveRole.kick();
}
catch (Exception e) {
Log.error(e);
}
} }
} }
endTime = System.currentTimeMillis(); endTime = System.currentTimeMillis();
// Removes the room from the list of rooms hosted in the server
MUCPersistenceManager.deleteFromDB(this);
server.removeChatRoom(name); server.removeChatRoom(name);
// Set that the room has been destroyed // Set that the room has been destroyed
isDestroyed = true; isDestroyed = true;
...@@ -683,6 +664,37 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -683,6 +664,37 @@ public class MUCRoomImpl implements MUCRoom {
finally { finally {
lock.writeLock().unlock(); lock.writeLock().unlock();
} }
// Send an unavailable presence to each removed occupant
for (MUCRole removedRole : removedRoles) {
try {
// Send a presence stanza of type "unavailable" to the occupant
Presence presence = createPresence(Presence.Type.unavailable);
presence.setFrom(removedRole.getRoleAddress());
// A fragment containing the x-extension for room destruction.
Element fragment = presence.addChildElement("x",
"http://jabber.org/protocol/muc#user");
Element item = fragment.addElement("item");
item.addAttribute("affiliation", "none");
item.addAttribute("role", "none");
if (alternateJID != null && alternateJID.length() > 0) {
fragment.addElement("destroy").addAttribute("jid", alternateJID);
}
if (reason != null && reason.length() > 0) {
Element destroy = fragment.element("destroy");
if (destroy == null) {
destroy = fragment.addElement("destroy");
}
destroy.addElement("reason").setText(reason);
}
removedRole.send(presence);
}
catch (Exception e) {
Log.error(e);
}
}
// Remove the room from the DB if the room was persistent
MUCPersistenceManager.deleteFromDB(this);
} }
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,37 +802,30 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -787,37 +802,30 @@ 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(); for (MUCRole occupant : occupants.values()) {
try { occupant.send(message);
for (MUCRole occupant : occupants.values()) { }
message.setTo(occupant.getChatUser().getAddress()); if (isLogEnabled()) {
router.route(message); MUCRole senderRole = null;
JID senderAddress = null;
if (message.getTo() != null && message.getTo().getResource() != null) {
senderRole = occupants.get(message.getTo().getResource());
} }
if (isLogEnabled()) { if (senderRole == null) {
MUCRole senderRole = null; // The room itself is sending the message
JID senderAddress = null; senderAddress = getRole().getRoleAddress();
if (message.getTo() != null && message.getTo().getResource() != null) {
senderRole = occupants.get(message.getTo().getResource());
}
if (senderRole == null) {
// The room itself is sending the message
senderAddress = getRole().getRoleAddress();
}
else {
// An occupant is sending the message
senderAddress = senderRole.getChatUser().getAddress();
}
// Log the conversation
server.logConversation(this, message, senderAddress);
} }
} else {
finally { // An occupant is sending the message
lock.readLock().unlock(); senderAddress = senderRole.getChatUser().getAddress();
}
// Log the conversation
server.logConversation(this, message, senderAddress);
} }
} }
...@@ -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