/* * Tigase Jabber/XMPP Server * Copyright (C) 2004-2012 "Artur Hefczyc" <artur.hefczyc@tigase.org> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. Look for COPYING file in the top folder. * If not, see http://www.gnu.org/licenses/. * * $Rev: 2411 $ * Last modified by $Author: kobit $ * $Date: 2010-10-27 20:27:58 -0600 (Wed, 27 Oct 2010) $ * */ package tigase.stats; import java.io.IOException; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.logging.Level; import java.util.logging.Logger; import javax.management.MBeanServerConnection; import javax.management.MBeanServerInvocationHandler; import javax.management.Notification; import javax.management.NotificationListener; import javax.management.ObjectName; import javax.management.remote.JMXConnectionNotification; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; import tigase.util.DataTypes; /** * @author Artur Hefczyc Created Jun 3, 2011 */ public class JavaJMXProxyOpt implements NotificationListener { private static final Logger log = Logger.getLogger(JavaJMXProxyOpt.class.getName()); private String id = null; private String hostname = null; private int port = -1; private String userName = null; private String password = null; private String urlPath = null; private JMXServiceURL jmxUrl = null; private JMXConnector jmxc = null; private long delay = -1; private long interval = -1; private boolean loadHistory = false; private boolean initialized = false; private StatisticsProviderMBean tigBean = null; private MBeanServerConnection server = null; private StatisticsUpdater updater = null; private Date lastDisconnectTime = null; private int cpuNo = 0; private List<JMXProxyListenerOpt> listeners = new LinkedList<JMXProxyListenerOpt>(); private Set<String> metrics = new LinkedHashSet<String>(); private Map<String, LinkedList<Object>> history = null; private String sysDetails = "No data yet..."; public JavaJMXProxyOpt(String id, String hostname, int port, String userName, String password, long delay, long interval, boolean loadHistory) { this.id = id; this.hostname = hostname; this.port = port; this.userName = userName; this.password = password; this.delay = delay; this.interval = interval; this.urlPath = "/jndi/rmi://" + this.hostname + ":" + this.port + "/jmxrmi"; this.loadHistory = loadHistory; System.out.println("Created: " + id + ":" + hostname + ":" + port); } public void addJMXProxyListener(JMXProxyListenerOpt listener) { listeners.add(listener); String[] dataIds = listener.getDataIds(); if (dataIds != null && dataIds.length > 0) { for (String did : dataIds) { metrics.add(did); } } } public void connect() throws Exception { this.jmxUrl = new JMXServiceURL("rmi", "", 0, this.urlPath); String[] userCred = new String[] { userName, password }; HashMap<String, Object> env = new HashMap<String, Object>(); env.put(JMXConnector.CREDENTIALS, userCred); jmxc = JMXConnectorFactory.newJMXConnector(jmxUrl, env); jmxc.addConnectionNotificationListener(this, null, null); jmxc.connect(); } public Map<String, String> getAllStats(int level) { if (tigBean != null) { return tigBean.getAllStats(level); } return null; } public Map<String, String> getComponentStats(String compName, int level) { if (tigBean != null) { return tigBean.getComponentStats(compName, level); } return null; } public List<String> getComponentsNames() { if (tigBean != null) { return tigBean.getComponentsNames(); } return null; } public String getId() { return id; } public void start() { if (updater == null) { updater = new StatisticsUpdater(); System.out.println("Started: " + id + ":" + hostname + ":" + port); } } @Override public void handleNotification(Notification notification, Object handback) { if (notification.getType().equals(JMXConnectionNotification.OPENED)) { System.out.println("Connected: " + id + ":" + hostname + ":" + port); try { server = jmxc.getMBeanServerConnection(); ObjectName obn = new ObjectName(StatisticsCollector.STATISTICS_MBEAN_NAME); tigBean = MBeanServerInvocationHandler.newProxyInstance(server, obn, StatisticsProviderMBean.class, false); if (history == null) { if (loadHistory) { String[] metrics_arr = metrics.toArray(new String[metrics.size()]); history = tigBean.getStatsHistory(metrics_arr); System.out.println(hostname + " loaded history, size: " + (history != null && history.get(metrics_arr[0]) != null ? history.get( metrics_arr[0]).size() : "null")); } else { System.out.println(hostname + " loading history switched off."); } if (history == null) { history = new LinkedHashMap<String, LinkedList<Object>>(); for (String m : metrics) { LinkedList<Object> list = new LinkedList<Object>(); history.put(m, list); } } } else { System.out.println(hostname + " history already loaded, skipping."); } for (JMXProxyListenerOpt jMXProxyListener : listeners) { jMXProxyListener.connected(id, this); } start(); } catch (Exception e) { e.printStackTrace(); } return; } if (notification.getType().equals(JMXConnectionNotification.CLOSED)) { server = null; tigBean = null; lastDisconnectTime = new Date(); for (JMXProxyListenerOpt jMXProxyListener : listeners) { jMXProxyListener.disconnected(id); } return; } if (notification.getType().equals(JMXConnectionNotification.FAILED)) { System.out.println("Reconnection to {hostName} failed..."); return; } System.out.println("Unsupported JMX notification: {notification.getType()}"); } public boolean isConnected() { return tigBean != null; } public boolean isInitialized() { return isConnected() && initialized; } public void update() { if (tigBean != null) { // This doesn't ever change so it is enough to query it once if (cpuNo == 0) { cpuNo = tigBean.getCPUsNumber(); } Map<String, Object> curMetrics = tigBean.getCurStats(metrics.toArray(new String[metrics.size()])); for (Map.Entry<String, Object> e : curMetrics.entrySet()) { LinkedList<Object> list = history.get(e.getKey()); if (list != null) { list.add(e.getValue()); if (list.size() > 1) { list.removeFirst(); } } } sysDetails = tigBean.getSystemDetails(); initialized = true; } } private class StatisticsUpdater { private Timer updateTimer = null; private StatisticsUpdater() { updateTimer = new Timer("stats-updater", true); // updateTimer.scheduleAtFixedRate(new TimerTask() { updateTimer.schedule(new TimerTask() { @Override public void run() { try { if (server == null) { connect(); } if (server != null) { update(); } } catch (IOException e) { Throwable cause = e; while (cause.getCause() != null) { cause = cause.getCause(); } String disconnected = ""; if (lastDisconnectTime != null) { long disconnectedInterval = (System.currentTimeMillis() - lastDisconnectTime.getTime()) / (1000 * 60); disconnected = ", disconnected: " + lastDisconnectTime + ", " + disconnectedInterval + " minutes ago."; } log.log(Level.WARNING, "{0}, {1}, retrying in {2} seconds{3}", new Object[] { cause.getMessage(), hostname, interval / 1000, disconnected }); // log.log(Level.FINEST, e.getMessage(), e); } catch (Exception e) { log.log(Level.WARNING, "Problem retrieving statistics: ", e); } } }, delay, interval); } } /** * @param string * @return */ public Object[] getMetricHistory(String key) { List<Object> result = history.get(key); if (result != null) { switch (DataTypes.decodeTypeIdFromName(key)) { case 'I': return result.toArray(new Integer[result.size()]); case 'L': return result.toArray(new Long[result.size()]); case 'F': return result.toArray(new Float[result.size()]); case 'D': return result.toArray(new Double[result.size()]); default: return result.toArray(new String[result.size()]); } } return null; } public String getHostname() { return hostname; } /** * @param string * @return */ public Object getMetricData(String key) { LinkedList<Object> h = history.get(key); if (h != null) { return h.getLast(); } return null; } /** * @return */ public String getSystemDetails() { return sysDetails; } }