/*
* Jicofo, the Jitsi Conference Focus.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.jicofo;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
* Class handled MUC roles and presence for the focus in particular:
* - ensures that focus has moderator role after MUC room is joined
* - elects moderator and makes sure that there is one during the conference
* - simplifies chat room events to 'member left', 'member joined'
*
* @author Pawel Domas
*/
public class ChatRoomRoleAndPresence
implements ChatRoomMemberPresenceListener,
ChatRoomMemberRoleListener,
ChatRoomLocalUserRoleListener
{
/**
* The logger
*/
private static final Logger logger
= Logger.getLogger(ChatRoomRoleAndPresence.class);
/**
* The {@link JitsiMeetConference} for which this instance is handling MUC
* related stuff.
*/
private final JitsiMeetConference conference;
/**
* The {@link ChatRoom} that is hosting Jitsi Meet conference.
*/
private final ChatRoom chatRoom;
/**
* The {@link ChatRoomMemberRole} of conference focus.
*/
private ChatRoomMemberRole focusRole;
/**
* Current moderator(other than the focus itself) of Jitsi Meet conference.
*/
private ChatRoomMember moderator;
public ChatRoomRoleAndPresence(JitsiMeetConference conference,
ChatRoom chatRoom)
{
this.conference = conference;
this.chatRoom = chatRoom;
}
/**
* Initializes this instance, so that it starts doing it's job.
*/
public void init()
{
chatRoom.addLocalUserRoleListener(this);
chatRoom.addMemberPresenceListener(this);
chatRoom.addMemberRoleListener(this);
}
/**
* Disposes resources used and stops any future processing that might have
* been done by this instance.
*/
public void dispose()
{
chatRoom.removelocalUserRoleListener(this);
chatRoom.removeMemberPresenceListener(this);
chatRoom.removeMemberRoleListener(this);
}
/**
* Analyzes chat room events and simplifies them into 'member joined',
* 'member left' and 'member kicked' events.
*
* {@inheritDoc}
*/
@Override
public void memberPresenceChanged(ChatRoomMemberPresenceChangeEvent evt)
{
logger.info("Chat room event " + evt);
logger.info("Participants count: "+evt.getChatRoom().getMembersCount());
ChatRoomMember sourceMember = evt.getChatRoomMember();
String eventType = evt.getEventType();
if (ChatRoomMemberPresenceChangeEvent.MEMBER_JOINED.equals(eventType))
{
if (moderator == null)
{
electNewModerator();
}
conference.onMemberJoined(sourceMember);
}
else if (ChatRoomMemberPresenceChangeEvent.MEMBER_LEFT.equals(eventType)
|| ChatRoomMemberPresenceChangeEvent.MEMBER_KICKED.equals(eventType)
|| ChatRoomMemberPresenceChangeEvent.MEMBER_QUIT.equals(eventType))
{
if (moderator == sourceMember)
{
logger.info("Moderator has left hte room !");
moderator = null;
electNewModerator();
}
if (ChatRoomMemberPresenceChangeEvent
.MEMBER_KICKED.equals(eventType))
{
conference.onMemberKicked(sourceMember);
}
else
{
conference.onMemberLeft(sourceMember);
}
}
else
{
logger.warn("Unhandled event: " + evt.getEventType());
}
}
/**
* Elects new moderator if the previous one has left the conference.
*/
private void electNewModerator()
{
if (focusRole == null)
{
// We don't know if we have permissions yet
return;
}
for (ChatRoomMember member : chatRoom.getMembers())
{
if (JitsiMeetConference.isFocusMember(member)
|| conference.isSipGateway(member))
{
continue;
}
else if (ChatRoomMemberRole.MODERATOR
.compareTo(member.getRole()) >=0)
{
// Select existing moderator
moderator = member;
logger.info(
"Moderator already in the room: " + member.getName());
break;
}
else
{
// Elect new moderator
try
{
chatRoom.grantModerator(member.getName());
logger.info(
"Granted moderator to " + member.getContactAddress());
moderator = member;
break;
}
catch (RuntimeException e)
{
logger.error(
"Failed to grant moderator status to "
+ member.getName(), e);
}
//break; FIXME: should cancel event if exception occurs ?
}
}
}
@Override
public void memberRoleChanged(ChatRoomMemberRoleChangeEvent evt)
{
logger.info("Role update event " + evt);
// FIXME: focus or moderator might loose it's privileges
// very unlikely(no such use case in client or anywhere in the app)
// but lets throw an exception or log fatal error at least to spare
// the time spent on debugging in future.
//ChatRoomMember member = evt.getSourceMember();
//if (JitsiMeetConference.isFocusMember(member))
//{
//}
}
/**
* Waits for initial focus role and refuses to join if moderator is
* not granted. Elects the first moderator of the conference.
*/
@Override
public void localUserRoleChanged(ChatRoomLocalUserRoleChangeEvent evt)
{
logger.info(
"Focus role: " + evt.getNewRole() + " init: " + evt.isInitial());
focusRole = evt.getNewRole();
if (ChatRoomMemberRole.MODERATOR.compareTo(focusRole) < 0)
{
logger.error("Focus must be a moderator!");
conference.stop();
return;
}
if (evt.isInitial() && moderator == null)
{
electNewModerator();
}
}
}