/*
* Copyright (C) 2008 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire.reporting.stats;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.archive.Conversation;
import org.jivesoftware.openfire.archive.ConversationManager;
import org.jivesoftware.openfire.archive.MonitoringConstants;
import org.jivesoftware.openfire.plugin.MonitoringPlugin;
import org.jivesoftware.openfire.reporting.graph.GraphEngine;
import org.jivesoftware.openfire.stats.Statistic;
import org.jivesoftware.openfire.user.UserNameManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils;
import org.xmpp.packet.JID;
/**
* Provides the server side callbacks for client side JavaScript functions for
* the stats dashboard page.
*
* @author Aaron Johnson
*/
public class StatsAction {
/**
* Retrieves a map containing the high / low and current count statistics
* for the 'sessions', 'conversations' and 'packet_count' statistics.
* @return map containing 3 maps (keys = 'sessions, 'conversations' and
* 'packet_count') each containing an array of int (low value, high value
* and current value).
*/
public Map<String, Map> getUpdatedStats(String timePeriod) {
Map<String, Map> results = new HashMap<String, Map>();
long[] startAndEnd = GraphEngine.parseTimePeriod(timePeriod);
String[] stats = new String[] {"sessions", "conversations", "packet_count",
"proxyTransferRate", "muc_rooms", "server_sessions", "server_bytes"};
for (String stat : stats) {
results.put(stat, getUpdatedStat(stat, startAndEnd));
}
return results;
}
/**
* Retrieve a a single stat update given a stat name and the name of a
* time period.
* @param statkey
* @param timePeriod
* @return map containing keys 'low', 'high' and 'count'.
*/
public Map getUpdatedStat(String statkey, String timePeriod) {
long[] startAndEnd = GraphEngine.parseTimePeriod(timePeriod);
return getUpdatedStat(statkey, startAndEnd);
}
private Map getUpdatedStat(String statkey, long[] timePeriod) {
MonitoringPlugin plugin = (MonitoringPlugin)XMPPServer.getInstance().getPluginManager().getPlugin(MonitoringConstants.NAME);
StatsViewer viewer = (StatsViewer)plugin.getModule(StatsViewer.class);
String[] lowHigh = getLowAndHigh(statkey, timePeriod);
Map stat = new HashMap();
stat.put("low", lowHigh[0]);
stat.put("high", lowHigh[1]);
stat.put("count", (int)viewer.getCurrentValue(statkey)[0]);
return stat;
}
/**
* Retrieves the last n conversations from the system that were created after
* the given conversationID.
*
* @param count the count of conversations to return.
* @param mostRecentConversationID the last conversationID that has been retrieved.
* @return a List of Map objects.
*/
public List<Map<String, Long>> getNLatestConversations(int count, long mostRecentConversationID) {
// TODO Fix plugin name 2 lines below and missing classes
List<Map<String, Long>> cons = new ArrayList<Map<String, Long>>();
MonitoringPlugin plugin = (MonitoringPlugin)XMPPServer.getInstance().getPluginManager().getPlugin(MonitoringConstants.NAME);
ConversationManager conversationManager = (ConversationManager)plugin.getModule(ConversationManager.class);
Collection<Conversation> conversations = conversationManager.getConversations();
List<Conversation> lConversations = Arrays.asList(conversations.toArray(new Conversation[conversations.size()]));
Collections.sort(lConversations, conversationComparator);
int counter = 0;
for (Iterator<Conversation> i = lConversations.iterator(); i.hasNext() && counter < count;) {
Conversation con = i.next();
if (mostRecentConversationID == con.getConversationID()) {
break;
} else {
Map mCon = new HashMap();
mCon.put("conversationid", con.getConversationID());
String users[];
int usersIdx = 0;
if (con.getRoom() == null) {
users = new String[con.getParticipants().size()];
for (JID jid : con.getParticipants()) {
String identifier = jid.toBareJID();
try {
identifier = UserNameManager.getUserName(jid, jid.toBareJID());
}
catch (UserNotFoundException e) {
// Ignore
}
users[usersIdx++] = StringUtils.abbreviate(identifier, 20);
}
}
else {
users = new String[2];
users[0] = LocaleUtils.getLocalizedString("dashboard.group_conversation", MonitoringConstants.NAME);
try {
users[1] = "(<i>" + LocaleUtils.getLocalizedString("muc.room.summary.room") +
": <a href='../../muc-room-occupants.jsp?roomName=" +
URLEncoder.encode(con.getRoom().getNode(), "UTF-8") + "'>" + con.getRoom().getNode() +
"</a></i>)";
} catch (UnsupportedEncodingException e) {
Log.error(e.getMessage(), e);
}
}
mCon.put("users", users);
mCon.put("lastactivity", formatTimeLong(con.getLastActivity()));
mCon.put("messages", con.getMessageCount());
cons.add(0, mCon);
counter++;
}
}
return cons;
}
/**
* Given a statistic key and a start date, end date and number of datapoints, returns
* a String[] containing the low and high values (in that order) for the given time period.
*
* @param key the name of the statistic to return high and low values for.
* @param timePeriod start date, end date and number of data points.
* @return low and high values for the given time period / number of datapoints
*/
public static String[] getLowAndHigh(String key, long[] timePeriod) {
MonitoringPlugin plugin = (MonitoringPlugin)XMPPServer.getInstance().getPluginManager().getPlugin(MonitoringConstants.NAME);
StatsViewer viewer = (StatsViewer)plugin.getModule(StatsViewer.class);
Statistic.Type type = viewer.getStatistic(key)[0].getStatType();
double[] lows = viewer.getMin(key, timePeriod[0], timePeriod[1], (int)timePeriod[2]);
double[] highs = viewer.getMax(key, timePeriod[0], timePeriod[1], (int)timePeriod[2]);
String low;
NumberFormat format = NumberFormat.getNumberInstance();
format.setMaximumFractionDigits(0);
if(lows.length > 0) {
if(type == Statistic.Type.count) {
double result = 0;
for (int i = 0; i < lows.length; i++ ) {
result += lows[i];
}
low = String.valueOf((int) result);
}
else {
double l = 0;
for (int i = 0; i < lows.length; i++ ) {
if(Double.isNaN(lows[i])) {
lows[i] = 0;
}
l += lows[i];
}
low = format.format(l);
}
}
else {
low = String.valueOf(0);
}
String high;
if(highs.length > 0) {
if(type == Statistic.Type.count) {
double result = 0;
for (int i=0; i < highs.length; i++ ) {
result += highs[i];
}
high = String.valueOf((int) result);
}
else {
double h= 0;
for (int i = 0; i < highs.length; i++) {
if(Double.isNaN(highs[i])) {
highs[i] = 0;
}
h += highs[i];
}
high = format.format(h);
}
}
else {
high = String.valueOf(0);
}
return new String[]{low, high};
}
private Comparator<Conversation> conversationComparator = new Comparator<Conversation>() {
public int compare(Conversation conv1, Conversation conv2) {
return conv2.getLastActivity().compareTo(conv1.getLastActivity());
}
};
/**
* Formats a given time using the <code>DateFormat.MEDIUM</code>. In the 'en' locale, this
* should result in a time formatted like this: 4:59:23 PM. The seconds are necessary when
* displaying time in the conversation scroller.
* @param time
* @return string a date formatted using DateFormat.MEDIUM
*/
public static String formatTimeLong(Date time) {
DateFormat formatter = DateFormat.getTimeInstance(DateFormat.MEDIUM, JiveGlobals.getLocale());
formatter.setTimeZone(JiveGlobals.getTimeZone());
return formatter.format(time);
}
}