Commit aa03ff5e authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

Added statistics.

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@3841 b35dd754-fafc-0310-a699-88a17e54d16e
parent fd99960a
...@@ -166,6 +166,11 @@ ...@@ -166,6 +166,11 @@
## Removed key: 'group.summary.list_group' ## Removed key: 'group.summary.list_group'
## Removed key: 'session.summary.info' ## Removed key: 'session.summary.info'
## Removed key: 'user.summary.info' ## Removed key: 'user.summary.info'
##
## 3.0.0
## Added key: 'startup.multiplexer'
## Added section: 'muc.stats..*'
## Removed key: 'pubsub.form.authorization.node'
# Wildfire # Wildfire
...@@ -291,6 +296,7 @@ startup.starting.muc=Multi User Chat domain: {0} ...@@ -291,6 +296,7 @@ startup.starting.muc=Multi User Chat domain: {0}
startup.caches=Initializing caches startup.caches=Initializing caches
startup.channels=Initializing channels startup.channels=Initializing channels
startup.server=Started server (unencrypted) socket on port: {0} startup.server=Started server (unencrypted) socket on port: {0}
startup.multiplexer=Started multiplexer (unencrypted) socket on port: {0}
startup.component=Started component (unencrypted) socket on port: {0} startup.component=Started component (unencrypted) socket on port: {0}
startup.plain=Started plain (unencrypted) socket on port: {0} startup.plain=Started plain (unencrypted) socket on port: {0}
startup.ssl=Started SSL (encrypted) socket on port: {0} startup.ssl=Started SSL (encrypted) socket on port: {0}
...@@ -854,6 +860,20 @@ muc.tasks.conversation.logging=Conversation Logging ...@@ -854,6 +860,20 @@ muc.tasks.conversation.logging=Conversation Logging
muc.tasks.flush=Flush interval (seconds): muc.tasks.flush=Flush interval (seconds):
muc.tasks.batch=Batch size: muc.tasks.batch=Batch size:
# MUC Statistics
muc.stats.occupants.name=Occupants
muc.stats.occupants.description=Total Room Occupants
muc.stats.occupants.label=Total Room Occupants
muc.stats.users.name=Users
muc.stats.users.description=Number of Connected Users
muc.stats.users.label=Number of Connected Users
muc.stats.incoming.name=Incoming Messages
muc.stats.incoming.description=Rate of Incoming Messages
muc.stats.incoming.label=Incoming Messages
muc.stats.outgoing.name=Outgoing Messages
muc.stats.outgoing.description=Rate of Outgoing Messages
muc.stats.outgoing.label=Outgoing Messages
# Offline messages Page # Offline messages Page
offline.messages.title=Offline Messages offline.messages.title=Offline Messages
...@@ -1723,7 +1743,6 @@ pubsub.form.subscription.keywords=Keyword to match ...@@ -1723,7 +1743,6 @@ pubsub.form.subscription.keywords=Keyword to match
pubsub.form.authorization.title=PubSub subscriber request pubsub.form.authorization.title=PubSub subscriber request
pubsub.form.authorization.instruction=Use the following form to approve or deny the subscription \ pubsub.form.authorization.instruction=Use the following form to approve or deny the subscription \
request. request.
pubsub.form.authorization.node=Node ID
pubsub.form.authorization.subscriber=Subscriber Address pubsub.form.authorization.subscriber=Subscriber Address
pubsub.form.authorization.allow=Allow this JID to subscribe to this pubsub node? pubsub.form.authorization.allow=Allow this JID to subscribe to this pubsub node?
pubsub.command.pending-subscriptions.label=Authorize Pending Subscriptions pubsub.command.pending-subscriptions.label=Authorize Pending Subscriptions
......
...@@ -82,6 +82,13 @@ public interface MUCUser extends ChannelHandler { ...@@ -82,6 +82,13 @@ public interface MUCUser extends ChannelHandler {
*/ */
void removeRole(String roomName); void removeRole(String roomName);
/**
* Returns true if the user is currently present in one or more rooms.
*
* @return true if the user is currently present in one or more rooms.
*/
boolean isJoined();
/** /**
* Get time (in milliseconds from System currentTimeMillis()) since last packet. * Get time (in milliseconds from System currentTimeMillis()) since last packet.
* *
......
...@@ -11,14 +11,14 @@ ...@@ -11,14 +11,14 @@
package org.jivesoftware.wildfire.muc; package org.jivesoftware.wildfire.muc;
import java.util.List;
import java.util.Collection;
import org.jivesoftware.wildfire.auth.UnauthorizedException; import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.user.UserNotFoundException; import org.jivesoftware.wildfire.user.UserNotFoundException;
import org.xmpp.packet.Message;
import org.xmpp.packet.JID;
import org.xmpp.component.Component; import org.xmpp.component.Component;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import java.util.Collection;
import java.util.List;
/** /**
* Manages groupchat conversations, chatrooms, and users. This class is designed to operate * Manages groupchat conversations, chatrooms, and users. This class is designed to operate
...@@ -283,4 +283,12 @@ public interface MultiUserChatServer extends Component { ...@@ -283,4 +283,12 @@ public interface MultiUserChatServer extends Component {
* @param sender the real XMPPAddress of the sender (e.g. john@example.org). * @param sender the real XMPPAddress of the sender (e.g. john@example.org).
*/ */
void logConversation(MUCRoom room, Message message, JID sender); void logConversation(MUCRoom room, Message message, JID sender);
/**
* Notification message indicating the server that an incoming message was broadcasted
* to a given number of occupants.
*
* @param numOccupants number of occupants that received the message.
*/
void messageBroadcastedTo(int numOccupants);
} }
\ No newline at end of file
...@@ -844,7 +844,7 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -844,7 +844,7 @@ public class MUCRoomImpl implements MUCRoom {
} }
if (isLogEnabled()) { if (isLogEnabled()) {
MUCRole senderRole = null; MUCRole senderRole = null;
JID senderAddress = null; JID senderAddress;
if (message.getTo() != null && message.getTo().getResource() != null) { if (message.getTo() != null && message.getTo().getResource() != null) {
senderRole = occupants.get(message.getTo().getResource()); senderRole = occupants.get(message.getTo().getResource());
} }
...@@ -859,6 +859,7 @@ public class MUCRoomImpl implements MUCRoom { ...@@ -859,6 +859,7 @@ public class MUCRoomImpl implements MUCRoom {
// Log the conversation // Log the conversation
server.logConversation(this, message, senderAddress); server.logConversation(this, message, senderAddress);
} }
server.messageBroadcastedTo(occupants.size());
} }
/** /**
......
...@@ -11,19 +11,21 @@ ...@@ -11,19 +11,21 @@
package org.jivesoftware.wildfire.muc.spi; package org.jivesoftware.wildfire.muc.spi;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import org.dom4j.Element; import org.dom4j.Element;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.wildfire.muc.*; import org.jivesoftware.util.Log;
import org.jivesoftware.util.*; import org.jivesoftware.util.NotFoundException;
import org.jivesoftware.wildfire.*; import org.jivesoftware.wildfire.PacketException;
import org.jivesoftware.wildfire.PacketRouter;
import org.jivesoftware.wildfire.auth.UnauthorizedException; import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.muc.*;
import org.jivesoftware.wildfire.user.UserAlreadyExistsException; import org.jivesoftware.wildfire.user.UserAlreadyExistsException;
import org.jivesoftware.wildfire.user.UserNotFoundException; import org.jivesoftware.wildfire.user.UserNotFoundException;
import org.xmpp.packet.*; import org.xmpp.packet.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* Implementation of MUCUser. There will be a MUCUser per user that is connected to one or more * Implementation of MUCUser. There will be a MUCUser per user that is connected to one or more
* rooms. A MUCUser contains a collection of MUCRoles for each room where the user has joined. * rooms. A MUCUser contains a collection of MUCRoles for each room where the user has joined.
...@@ -74,6 +76,10 @@ public class MUCUserImpl implements MUCUser { ...@@ -74,6 +76,10 @@ public class MUCUserImpl implements MUCUser {
return role; return role;
} }
public boolean isJoined() {
return !roles.isEmpty();
}
public Iterator<MUCRole> getRoles() { public Iterator<MUCRole> getRoles() {
return Collections.unmodifiableCollection(roles.values()).iterator(); return Collections.unmodifiableCollection(roles.values()).iterator();
} }
......
...@@ -29,6 +29,8 @@ import org.jivesoftware.wildfire.forms.FormField; ...@@ -29,6 +29,8 @@ import org.jivesoftware.wildfire.forms.FormField;
import org.jivesoftware.wildfire.forms.spi.XDataFormImpl; import org.jivesoftware.wildfire.forms.spi.XDataFormImpl;
import org.jivesoftware.wildfire.forms.spi.XFormFieldImpl; import org.jivesoftware.wildfire.forms.spi.XFormFieldImpl;
import org.jivesoftware.wildfire.muc.*; import org.jivesoftware.wildfire.muc.*;
import org.jivesoftware.wildfire.stats.Statistic;
import org.jivesoftware.wildfire.stats.StatisticsManager;
import org.jivesoftware.wildfire.user.UserNotFoundException; import org.jivesoftware.wildfire.user.UserNotFoundException;
import org.xmpp.component.ComponentManager; import org.xmpp.component.ComponentManager;
import org.xmpp.packet.*; import org.xmpp.packet.*;
...@@ -37,6 +39,8 @@ import java.util.*; ...@@ -37,6 +39,8 @@ import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/** /**
* Implements the chat server as a cached memory resident chat server. The server is also * Implements the chat server as a cached memory resident chat server. The server is also
...@@ -59,6 +63,17 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha ...@@ -59,6 +63,17 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha
private static final FastDateFormat dateFormatter = FastDateFormat private static final FastDateFormat dateFormatter = FastDateFormat
.getInstance("yyyyMMdd'T'HH:mm:ss", TimeZone.getTimeZone("GMT+0")); .getInstance("yyyyMMdd'T'HH:mm:ss", TimeZone.getTimeZone("GMT+0"));
/**
* Statistics keys
*/
private static final String roomsStatKey = "muc_rooms";
private static final String occupantsStatKey = "muc_occupants";
private static final String usersStatKey = "muc_users";
private static final String incomingStatKey = "muc_incoming";
private static final String outgoingStatKey = "muc_outgoing";
/** /**
* The time to elapse between clearing of idle chat users. * The time to elapse between clearing of idle chat users.
*/ */
...@@ -167,6 +182,17 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha ...@@ -167,6 +182,17 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha
*/ */
private final long cleanup_frequency = 60 * 60 * 1000; private final long cleanup_frequency = 60 * 60 * 1000;
/**
* Total number of received messages in all rooms since the last reset. The counter
* is reset each time the Statistic makes a sampling.
*/
private AtomicInteger inMessages = new AtomicInteger(0);
/**
* Total number of broadcasted messages in all rooms since the last reset. The counter
* is reset each time the Statistic makes a sampling.
*/
private AtomicLong outMessages = new AtomicLong(0);
/** /**
* Create a new group chat server. * Create a new group chat server.
*/ */
...@@ -294,14 +320,14 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha ...@@ -294,14 +320,14 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha
for (MUCUser user : users.values()) { for (MUCUser user : users.values()) {
try { try {
if (user.getLastPacketTime() < deadline) { if (user.getLastPacketTime() < deadline) {
// Kick the user from all the rooms that he/she had previuosly joined
Iterator<MUCRole> roles = user.getRoles();
// If user is not present in any room then remove the user from // If user is not present in any room then remove the user from
// the list of users // the list of users
if (!roles.hasNext()) { if (!user.isJoined()) {
removeUser(user.getAddress()); removeUser(user.getAddress());
continue; continue;
} }
// Kick the user from all the rooms that he/she had previuosly joined
Iterator<MUCRole> roles = user.getRoles();
MUCRole role; MUCRole role;
MUCRoom room; MUCRoom room;
Presence kickedPresence; Presence kickedPresence;
...@@ -781,6 +807,12 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha ...@@ -781,6 +807,12 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha
router)) { router)) {
rooms.put(room.getName().toLowerCase(), room); rooms.put(room.getName().toLowerCase(), room);
} }
// Add statistics
addTotalRoomStats();
addTotalOccupantsStats();
addTotalConnectedUsers();
addNumberIncomingMessages();
addNumberOutgoingMessages();
} }
public void stop() { public void stop() {
...@@ -789,16 +821,69 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha ...@@ -789,16 +821,69 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha
routingTable.removeRoute(getAddress()); routingTable.removeRoute(getAddress());
timer.cancel(); timer.cancel();
logAllConversation(); logAllConversation();
// Remove the statistics.
StatisticsManager.getInstance().removeStatistic(roomsStatKey);
StatisticsManager.getInstance().removeStatistic(occupantsStatKey);
StatisticsManager.getInstance().removeStatistic(usersStatKey);
StatisticsManager.getInstance().removeStatistic(incomingStatKey);
StatisticsManager.getInstance().removeStatistic(outgoingStatKey);
} }
public long getTotalChatTime() { public long getTotalChatTime() {
return totalChatTime; return totalChatTime;
} }
/**
* Retuns the number of existing rooms in the server (i.e. persistent or not,
* in memory or not).
*
* @return the number of existing rooms in the server.
*/
public int getNumberChatRooms() {
return rooms.size();
}
/**
* Retuns the total number of occupants in all rooms in the server.
*
* @return the number of existing rooms in the server.
*/
public int getNumberConnectedUsers() {
int total = 0;
for (MUCUser user : users.values()) {
if (user.isJoined()) {
total = total + 1;
}
}
return total;
}
/**
* Retuns the total number of users that have joined in all rooms in the server.
*
* @return the number of existing rooms in the server.
*/
public int getNumberRoomOccupants() {
int total = 0;
for (MUCRoom room : rooms.values()) {
total = total + room.getOccupantsCount();
}
return total;
}
public void logConversation(MUCRoom room, Message message, JID sender) { public void logConversation(MUCRoom room, Message message, JID sender) {
logQueue.add(new ConversationLogEntry(new Date(), room, message, sender)); logQueue.add(new ConversationLogEntry(new Date(), room, message, sender));
} }
public void messageBroadcastedTo(int numOccupants) {
// Increment counter of received messages that where broadcasted by one
inMessages.incrementAndGet();
// Increment counter of outgoing messages with the number of room occupants
// that received the message
outMessages.addAndGet(numOccupants);
}
public Iterator<DiscoServerItem> getItems() { public Iterator<DiscoServerItem> getItems() {
ArrayList<DiscoServerItem> items = new ArrayList<DiscoServerItem>(); ArrayList<DiscoServerItem> items = new ArrayList<DiscoServerItem>();
...@@ -1038,4 +1123,157 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha ...@@ -1038,4 +1123,157 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha
} }
return buf.toString(); return buf.toString();
} }
/****************** Statistics code ************************/
private void addTotalRoomStats() {
// Register a statistic.
Statistic statistic = new Statistic() {
public String getKey() {
return roomsStatKey;
}
public String getName() {
return LocaleUtils.getLocalizedString("muc.room.summary.total_room");
}
public Type getStatType() {
return Type.COUNT;
}
public String getDescription() {
return LocaleUtils.getLocalizedString("muc.room.summary.total_room");
}
public String getUnits() {
return LocaleUtils.getLocalizedString("muc.room.summary.total_room");
}
public double sample(long timePeriod) {
return getNumberChatRooms();
}
};
StatisticsManager.getInstance().addStatistic(statistic);
}
private void addTotalOccupantsStats() {
// Register a statistic.
Statistic statistic = new Statistic() {
public String getKey() {
return occupantsStatKey;
}
public String getName() {
return LocaleUtils.getLocalizedString("muc.stats.occupants.name");
}
public Type getStatType() {
return Type.COUNT;
}
public String getDescription() {
return LocaleUtils.getLocalizedString("muc.stats.occupants.description");
}
public String getUnits() {
return LocaleUtils.getLocalizedString("muc.stats.occupants.label");
}
public double sample(long timePeriod) {
return getNumberRoomOccupants();
}
};
StatisticsManager.getInstance().addStatistic(statistic);
}
private void addTotalConnectedUsers() {
// Register a statistic.
Statistic statistic = new Statistic() {
public String getKey() {
return usersStatKey;
}
public String getName() {
return LocaleUtils.getLocalizedString("muc.stats.users.name");
}
public Type getStatType() {
return Type.COUNT;
}
public String getDescription() {
return LocaleUtils.getLocalizedString("muc.stats.users.description");
}
public String getUnits() {
return LocaleUtils.getLocalizedString("muc.stats.users.label");
}
public double sample(long timePeriod) {
return getNumberConnectedUsers();
}
};
StatisticsManager.getInstance().addStatistic(statistic);
}
private void addNumberIncomingMessages() {
// Register a statistic.
Statistic statistic = new Statistic() {
public String getKey() {
return incomingStatKey;
}
public String getName() {
return LocaleUtils.getLocalizedString("muc.stats.incoming.name");
}
public Type getStatType() {
return Type.RATE;
}
public String getDescription() {
return LocaleUtils.getLocalizedString("muc.stats.incoming.description");
}
public String getUnits() {
return LocaleUtils.getLocalizedString("muc.stats.incoming.label");
}
public double sample(long timePeriod) {
int received = inMessages.getAndSet(0);
return received/timePeriod;
}
};
StatisticsManager.getInstance().addStatistic(statistic);
}
private void addNumberOutgoingMessages() {
// Register a statistic.
Statistic statistic = new Statistic() {
public String getKey() {
return outgoingStatKey;
}
public String getName() {
return LocaleUtils.getLocalizedString("muc.stats.outgoing.name");
}
public Type getStatType() {
return Type.RATE;
}
public String getDescription() {
return LocaleUtils.getLocalizedString("muc.stats.outgoing.description");
}
public String getUnits() {
return LocaleUtils.getLocalizedString("muc.stats.outgoing.label");
}
public double sample(long timePeriod) {
long received = outMessages.getAndSet(0);
return received/timePeriod;
}
};
StatisticsManager.getInstance().addStatistic(statistic);
}
} }
\ No newline at end of file
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