/* * 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); } }