Commit bfb9da80 authored by ncampbell's avatar ncampbell

Initial checkin of meter. This is a WIP.

git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/branches/pampero@2852 b35dd754-fafc-0310-a699-88a17e54d16e
parent 77812110
initialize.objectname=Loading {0}.
rrdmanager.createoverride=Using override {0}.
rrdmanager.archiveadded=Adding archive {0}.
accumulator.resultnotnumber=Unable to update RRD {2}. Object returned was type {0} with result {1}.
accumulator.exprresults={0}({1}) result {2}.
accumulator.getvaluefailed={0} with expression: {1} failed: {2}.
accumulatorproxy.nullresult=Null result returned from {0} with expression {1}.
\ No newline at end of file
java.lang\:type\=Threading=org.jivesoftware.messenger.plugin.monitor.ThreadMXBeanSupportInfo
java.lang\:type\=Memory=org.jivesoftware.messenger.plugin.monitor.MemoryMXBeanSupportInfo
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<class>org.jivesoftware.messenger.plugin.monitor.MonitorPlugin</class>
<name>Monitor</name>
<description>Provides in depth monitoring of various runtime components in Messenger.</description>
<author>Noah Campbell</author>
<version>1.0</version>
<date>09/01/2005</date>
<minServerVersion>2.1.5</minServerVersion>
<adminconsole>
<tab id="tab-server">
<sidebar id="sidebar-server-settings">
<item id="monitor-service" name="Core Monitor Service" url="graphsetup.jsp"
description="Click to configure the monitor service." />
</sidebar>
</tab>
</adminconsole>
</plugin>
\ No newline at end of file
/**
*
*/
package org.jivesoftware.messenger.plugin.meter;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.ObjectName;
import org.jrobin.annotations.Arc;
import org.jrobin.annotations.Ds;
import org.jrobin.annotations.Rrd;
import org.jrobin.core.RrdDb;
import org.jrobin.core.RrdDbPool;
import org.jrobin.core.RrdDef;
import org.jrobin.core.RrdException;
import org.jrobin.core.Sample;
/**
* @author Noah Campbell
* @version 1.0
*/
public class Accumulator implements Runnable {
/** The logger. */
final static private Logger logger = Logger.getLogger("ACCUMULATOR", "resources");
/** The dbPath. */
private final String dbPath;
/** The objectName. */
private final ObjectName objectName;
/** The pool. */
private final RrdDbPool pool;
/**
* Construct a new <code>Accumulator</code>.
*
* @param objectName
* @param db
*/
public Accumulator(ObjectName objectName, String db) {
this.dbPath = db;
this.objectName = objectName;
this.pool = RrdDbPool.getInstance();
}
/** The lastTimestamp. */
private long lastTimestamp = Long.MIN_VALUE;
/**
* @see java.lang.Runnable#run()
*/
public void run() {
RrdDb db = null;
try {
db = pool.requestRrdDb(dbPath);
} catch (IOException e1) {
logger.log(Level.SEVERE, "accumulator.unabletoopenrrd", new Object[]{dbPath,
e1});
return;
} catch (RrdException e1) {
logger.log(Level.SEVERE, "accumulator.unabletoprocessrrd", new Object[]{dbPath,
e1});
return;
}
try {
long timestamp = 0;
Sample s = db.createSample();
timestamp = s.getTime();
AccumulatorHelper helper = (AccumulatorHelper) AccumulatorManager.getAccumulator(this.objectName);
Map<String, Number> results = helper.getResults();
for(String key : results.keySet()) {
s.setValue(key, results.get(key).doubleValue());
}
if( timestamp == lastTimestamp) {
logger.log(Level.INFO,"accumulator.rush", new Object[]{new Date(timestamp * 1000),
this.objectName, this.dbPath,
Thread.currentThread().getName(),
Thread.currentThread().getId()});
Thread.sleep(1000);
}
s.update();
lastTimestamp = timestamp;
} catch (IOException e) {
logger.log(Level.WARNING, "accumulator.ioexception", e);
} catch (RrdException e) {
logger.log(Level.WARNING, "accumulator.rrdexception", e);
} catch (IllegalArgumentException e) {
logger.log(Level.WARNING, "accumulator.illegalargumentexceptin", e);
} catch (SecurityException e) {
logger.log(Level.WARNING, "accumulator.securityexception", e);
} catch (InterruptedException e) {
logger.log(Level.FINE, "accumulator.interrupted",e.getLocalizedMessage());
} catch (UnableToAllocateAccumulator e) {
logger.log(Level.WARNING, "accumulator.allocateaccumulatorfailed", e.getLocalizedMessage());
} catch (AccumulatorDefinitionNotFoundException e) {
logger.log(Level.WARNING, "accumulator.allocatornotfound", e.getLocalizedMessage());
} finally {
try {
pool.release(db);
} catch (Exception e) {
logger.log(Level.WARNING, "accumulator.releasefailed", e);
}
}
}
}
/**
*
*/
package org.jivesoftware.messenger.plugin.meter;
/**
* @author Noah Campbell
* @version 1.0
*/
public class AccumulatorDefinitionNotFoundException extends Exception {
/** The serialVersionUID. */
private static final long serialVersionUID = 1L;
/**
* Construct a new <code>AccululationDefinitionNotFoundException</code>.
*
*/
public AccumulatorDefinitionNotFoundException() {
super();
// TODO Auto-generated constructor stub
}
/**
* Construct a new <code>AccululationDefinitionNotFoundException</code>.
*
* @param message
*/
public AccumulatorDefinitionNotFoundException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
/**
* Construct a new <code>AccululationDefinitionNotFoundException</code>.
*
* @param message
* @param cause
*/
public AccumulatorDefinitionNotFoundException(String message,
Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
/**
* Construct a new <code>AccululationDefinitionNotFoundException</code>.
*
* @param cause
*/
public AccumulatorDefinitionNotFoundException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
/**
*
*/
package org.jivesoftware.messenger.plugin.meter;
import java.util.Map;
/**
* @author Noah Campbell
* @version 1.0
*/
interface AccumulatorHelper {
/**
* <code>getResults</code> returns a map of datasource name and the corresponding
* value for the paticular Accumulator. This is a help method to make the
* extract of an arbitrary accumulator more easily assasible.
*
* @return map A map of the results.
*/
Map<String, Number> getResults();
}
/**
*
*/
package org.jivesoftware.messenger.plugin.meter;
import java.lang.management.ManagementFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.apache.commons.jxpath.CompiledExpression;
import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.JXPathException;
import org.jrobin.annotations.Ds;
import org.jrobin.annotations.Rrd;
/**
* @author Noah Campbell
* @version 1.0
*/
public class AccumulatorManager {
/** The overrides. */
private static Map<ObjectName, Class> overrides = new HashMap<ObjectName, Class>();
/**
* @param name
* @return cls The class that overrides the default MBeanInfo.getClassName
*/
public static Class getOverride(ObjectName name) {
return overrides.get(name);
}
/**
* @param objectName
* @param cls
*/
public static void registerOverride(ObjectName objectName, Class cls) {
overrides.put(objectName, cls);
}
/**
* @param objectName
*/
public static void deregisterOverride(ObjectName objectName) {
overrides.remove(objectName);
}
/**
* @author Noah Campbell
* @version 1.0
*/
static final class AccumulatorProxy implements InvocationHandler {
/** The mbean server. */
private final static MBeanServer server = ManagementFactory.getPlatformMBeanServer();
/**
* Construct a new <code>AccumulatorProxy</code>.
*
* @param objectName
* @param override
* @throws AccumulatorDefinitionNotFoundException
*/
@SuppressWarnings("unchecked")
public AccumulatorProxy(ObjectName objectName, Class override) throws AccumulatorDefinitionNotFoundException {
this.objectName = objectName;
Class cls = null;
if(override == null) {
try {
Class overrideClass = overrides.get(objectName);
if(overrideClass != null) {
cls = overrideClass;
} else {
// last ditch effort.
cls = Class.forName(server.getMBeanInfo(objectName).getClassName());
}
} catch (Exception e) {
throw new AccumulatorDefinitionNotFoundException(e);
}
} else {
cls = override;
}
if(cls == null) {
throw new AccumulatorDefinitionNotFoundException("Unable to locate class");
}
if(cls.getAnnotation(Rrd.class) == null) {
throw new IllegalArgumentException("No @Rrd specified");
}
accumulator = java.lang.reflect.Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[]{cls, AccumulatorHelper.class},
this);
this.mbeanClass = cls;
context = JXPathContext.newContext(this.objectName);
}
/**
* Construct a new <code>AccumulatorProxy</code>.
*
* @param name
* @throws Exception
*/
public AccumulatorProxy(ObjectName name) throws Exception {
this(name, null);
}
/** The accumulator. */
private final Object accumulator;
/** The objectName. */
private final ObjectName objectName;
/** The mbeanClass. */
private final Class mbeanClass;
/** The context. */
private JXPathContext context = null;
// private Map<Method, CompiledExpression> methodCache = new HashMap<Method, CompiledExpression>();
/**
* @return accumulator The accumulator.
*/
public Object getAccumulator() {
return this.accumulator;
}
/**
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke(@SuppressWarnings("unused") Object proxy, Method method, @SuppressWarnings("unused") Object[] args)
throws Throwable {
if(method.getName().equals("getResults")) {
return getResults();
}
try {
Ds ds = method.getAnnotation(Ds.class);
return extract(ds);
} catch (Exception e) {
return 0;
}
}
/** ZERO. Declared once, used many. */
private static final Number ZERO = new Double(0.0d);
/**
* @param ds The \@Ds to extract.
* @return number The value or 0 if the result is not of type Number.
*/
private Number extract(Ds ds) {
try {
CompiledExpression e = JXPathContext.compile(ds.expr());
Object result = e.getValue(context);
if(result != null) {
logger.log(Level.INFO, "accumulator.exprresults", new Object[]{ds.name(),
ds.expr(), result});
return (Number) result;
} else {
logger.log(Level.WARNING, "accumulatorproxy.nullresult",
new Object[] { ds.name(), ds.expr() });
}
} catch (JXPathException e) {
logger.log(Level.WARNING, "accumulator.getvaluefailed",
new Object[]{ds.name(),
ds.expr(),
e.getLocalizedMessage()});
} catch (Exception e) {
logger.log(Level.WARNING, "accumulatorproxy.unabletoextract",
new Object[]{ds.name(), ds.expr(), e.getLocalizedMessage()});
}
return ZERO;
}
/**
* @return map A map of results.
*/
private Map<String, Number> getResults() {
Map<String, Number> results = new HashMap<String, Number>();
List<Ds> dss = findDs(this.mbeanClass);
for(Ds ds : dss) {
results.put(ds.name(), extract(ds));
}
return results;
}
/**
* @param forName
* @return dss List of Ds.
*/
private List<Ds> findDs(Class<?> forName) {
List<Ds> anonDs = new ArrayList<Ds>();
Method[] methods = forName.getMethods();
for(Method m : methods) {
Ds ds = m.getAnnotation(Ds.class);
if(ds != null) {
if(m.getReturnType().isPrimitive() || m.getReturnType().isAssignableFrom(Number.class)) {
anonDs.add(ds);
} else {
logger.log(Level.WARNING, "accumulatorproxy.invalidreturntype", ds.name());
}
}
}
return anonDs;
}
}
/** The logger. */
final static private Logger logger = Logger.getLogger("accumulatormanager",
"resources");
/** The accumulators. */
private static Map<ObjectName, AccumulatorProxy> accumulators = new ConcurrentHashMap<ObjectName, AccumulatorProxy>();
/**
* @param objectName
* @param override
* @return accumulator
* @throws UnableToAllocateAccumulator
* @throws AccumulatorDefinitionNotFoundException
*/
@SuppressWarnings("unused")
public static Object getAccumulator(ObjectName objectName, Class override) throws UnableToAllocateAccumulator, AccumulatorDefinitionNotFoundException {
if(!accumulators.containsKey(objectName)) {
accumulators.put(objectName, new AccumulatorProxy(objectName, override));
}
return accumulators.get(objectName).getAccumulator();
}
/**
* @param objectName
* @return accumulator Return the accumulator.
* @throws AccumulatorDefinitionNotFoundException
* @throws UnableToAllocateAccumulator
*/
public static Object getAccumulator(ObjectName objectName) throws UnableToAllocateAccumulator, AccumulatorDefinitionNotFoundException {
return getAccumulator(objectName, null);
}
}
/**
*
*/
package org.jivesoftware.messenger.plugin.meter;
import java.util.ArrayList;
import java.util.Set;
import javax.management.openmbean.CompositeData;
import org.apache.commons.jxpath.DynamicPropertyHandler;
/**
* @author Noah Campbell
* @version 1.0
*/
public class CompositeDataPropertyHandler implements DynamicPropertyHandler {
/**
* @see org.apache.commons.jxpath.DynamicPropertyHandler#getPropertyNames(java.lang.Object)
*/
public String[] getPropertyNames(Object object) {
ArrayList<String> names = new ArrayList<String>(20);
if(object instanceof CompositeData) {
CompositeData cd = (CompositeData)object;
Set keys = cd.getCompositeType().keySet();
for(Object key : keys) {
names.add((String) key);
}
}
return names.toArray(new String[names.size()]);
}
/**
* @see org.apache.commons.jxpath.DynamicPropertyHandler#getProperty(java.lang.Object, java.lang.String)
*/
public Object getProperty(Object object, String propertyName) {
if(object instanceof CompositeData) {
CompositeData cd = (CompositeData)object;
return cd.get(propertyName);
}
return null;
}
/**
* @see org.apache.commons.jxpath.DynamicPropertyHandler#setProperty(java.lang.Object, java.lang.String, java.lang.Object)
*/
public void setProperty(@SuppressWarnings("unused") Object object,
@SuppressWarnings("unused") String propertyName,
@SuppressWarnings("unused") Object value) {
throw new UnsupportedOperationException("read-only");
}
}
/**
*
*/
package org.jivesoftware.messenger.plugin.meter;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.management.MBeanInfo;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.jrobin.annotations.Rrd;
/**
* @author Noah Campbell
* @version 1.0
*/
public class GraphBuilderBean {
/**
* Return all the ObjectNames
*
* @return objectnames
* @throws Exception
*/
public List<String> getChartableMBeans() throws Exception {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
Set mbeans = server.queryNames(null, null);
List<String> results = new ArrayList<String>(mbeans.size());
for(Object mbean : mbeans) {
ObjectName objectName = ((ObjectName)mbean);
MBeanInfo info = server.getMBeanInfo(objectName);
Rrd rrd = info.getClass().getAnnotation(Rrd.class);
if(rrd != null)
results.add(objectName.getCanonicalName());
}
return results;
}
public List<String> getStores() {
return new ArrayList<String>(RrdManager.listStores().keySet());
}
}
/**
*
*/
package org.jivesoftware.messenger.plugin.meter;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.imageio.ImageIO;
import javax.management.ObjectName;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.jrobin.core.RrdDb;
import org.jrobin.core.RrdDbPool;
import org.jrobin.graph.RrdGraph;
import org.jrobin.graph.RrdGraphDef;
/**
* @author Noah Campbell
* @version 1.0
*/
public class GraphServlet extends HttpServlet {
/** The serialVersionUID. */
private static final long serialVersionUID = 1L;
/**
* @param request The Request.
* @param response The Response.
* @throws ServletException Throw if unable to process the request due to a Servlet related problem.
* @throws IOException Thrown if unable to process the request due to an IO related problem.
* @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@SuppressWarnings("unused")
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("image/png");
ServletOutputStream out = response.getOutputStream();
response.addHeader("Cache-Control", "no-cache");
createPNGGraph(request, out);
}
/** The colors. */
private static final Color[] colors = new Color[]{new Color(102, 204, 204),
new Color(102,153,204),
new Color(102,102,204),
new Color(153,102,204),
new Color(102,204,153),
new Color( 61,184,184),
new Color( 46,138,138),
new Color(204,102,204),
new Color(102,204,102),
new Color(138, 46, 46),
new Color(184, 61, 61),
new Color(204,102,153),
new Color(153,204,102),
new Color(204,204,102),
new Color(204,153,102),
new Color(204,102,102)};
/** The converter. */
private static final Converter converter = new ColorConverter();
/**
* @param request The servlet request.
* @param out The output stream.
* @throws IOException Thrown if an IO exception occurs.
*/
private void createPNGGraph(HttpServletRequest request, OutputStream out) throws IOException {
int width;
int height;
try {
int w = Integer.parseInt(request.getParameter("width"));
width = w <= 800 ? w : 800;
} catch (NumberFormatException e) {
width = 400;
}
try {
int h = Integer.parseInt(request.getParameter("height"));
height = h <= 600 ? h : 600;
} catch (NumberFormatException e) {
height = 300;
}
String error = "An error has occured.";
try {
ConvertUtils.register(converter, Color.class);
String store = request.getParameter("store");
String objectName = request.getParameter("oname");
RrdGraphDef def = new RrdGraphDef();
BeanUtilsBean.getInstance().populate(def, request.getParameterMap());
String[] lines = request.getParameterValues("lines");
for(String line : lines) {
String[] field = line.split("::");
def.line(field[0], parseColor(field[1]), field[2]);
}
RrdDbPool pool = RrdDbPool.getInstance();
RrdManager manager = RrdManager.listStores().get(store);
String filename = manager.getFileName(new ObjectName(objectName));
RrdDb db = pool.requestRrdDb(filename);
String ref = "input";
int count = 0;
for(String ds : db.getDsNames()) {
String id = ref + count++;
def.datasource(id, filename, ds, "AVERAGE");
def.line(id, colors[count], ds);
}
RrdGraph graph = new RrdGraph(def);
byte[] output;
if(request.getParameter("width") == null) {
output = graph.getPNGBytes();
} else {
output = graph.getPNGBytes(width, height);
}
/**
* write out the byte array to the client.
*/
ByteArrayInputStream bis = new ByteArrayInputStream(output);
while(bis.available() > 0) {
out.write(bis.read());
}
} catch (Exception e) {
error += " " + e.getLocalizedMessage();
} finally {
ConvertUtils.deregister(Color.class);
}
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D) bufferedImage.getGraphics();
g2d.setBackground(Color.white);
// g2d.setBackground(new Color(0,0,0,255));
// GradientPaint gpaint = new GradientPaint(0, height / 2, Color.white, width, height / 2, Color.gray);
// Paint originalPaint = g2d.getPaint();
// g2d.setPaint(gpaint);
g2d.fillRect(0,0,width,height);
int errorWidth = g2d.getFontMetrics().stringWidth(error);
// g2d.setPaint(originalPaint);
g2d.setColor(Color.black);
g2d.drawString(error, width / 2 - errorWidth / 2, height / 2);
g2d.dispose();
ImageIO.write(bufferedImage, "png", out);
}
/**
* @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
*/
@Override
public void init(ServletConfig arg0) throws ServletException {
// TODO Auto-generated method stub
super.init(arg0);
}
/**
* @author Noah Campbell
* @version 1.0
*/
private static class ColorConverter implements Converter {
/**
* @see org.apache.commons.beanutils.Converter#convert(java.lang.Class, java.lang.Object)
*/
public Object convert(@SuppressWarnings("unused") Class cls, Object input) {
if(input instanceof String) {
String s = (String)input;
return parseColor(s);
}
return colors[0];
}
}
/**
* Parse a string of format #RRBBGGAA where RR, BB, GG, AA are hex values
* ranging between 0 (00) and 255 (FF). AA is optional and can be excluded
* from the #RRBBGG string.
*
* @param s
* @return color
*/
private static Color parseColor(String s) {
try {
if( s.startsWith("#") ) {
int r = Integer.valueOf(s.substring(1, 3), 16);
int g = Integer.valueOf(s.substring(3, 5), 16);
int b = Integer.valueOf(s.substring(5, 7), 16);
int a = 255;
if(s.length() > 7) {
a = Integer.valueOf(s.substring(7, 9), 16);
}
return new Color(r, g, b, a);
}
} catch (RuntimeException e) {
}
return colors[0];
}
}
/**
*
*/
package org.jivesoftware.messenger.plugin.meter;
import org.jrobin.annotations.Ds;
import org.jrobin.annotations.Rrd;
import org.jrobin.annotations.SourceType;
/**
* @author Noah Campbell
* @version 1.0
*/
@Rrd
public interface MemoryMXBeanSupportInfo {
/**
* @return max The max heap memory usage.
*/
@Ds(name="max", expr="/HeapMemoryUsage/max", type=SourceType.COUNTER)
long getMaxHeapMemoryUsage();
/**
* @return initial The initial heap memory usage.
*/
@Ds(name="init", expr="/HeapMemoryUsage/init")
long getInitHeapMemoryUsage();
/**
* @return used The heap memory usage.
*/
@Ds(name="used", expr="/HeapMemoryUsage/used")
long getUsedHeapMemoryUsage();
/**
* @return commited The commited heap memory usage.
*/
@Ds(name="commited", expr="/HeapMemoryUsage/committed")
long getCommittedHeapMemoryUsage();
}
/**
*
*/
package org.jivesoftware.messenger.plugin.meter;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import org.jivesoftware.messenger.container.Plugin;
import org.jivesoftware.messenger.container.PluginManager;
import org.jrobin.core.RrdDbPool;
import org.jrobin.core.RrdException;
/**
* @author Noah Campbell
* @version 1.0
*/
public class MeterPlugin implements Plugin {
/** The pluginDirectory. */
@SuppressWarnings("unused")
private File pluginDirectory;
/** The manager. */
@SuppressWarnings("unused")
private PluginManager manager;
/** The logger. */
final static private Logger logger = Logger.getLogger("monitorplugin", "resources");
/** The server. */
private MBeanServer server = ManagementFactory.getPlatformMBeanServer();
/** The preferences. */
private Preferences preferences;
/** The rrdManager. */
private RrdManager rrdManager;
/** The executors. */
ScheduledExecutorService executors = Executors.newScheduledThreadPool(10);
/** The pool. */
RrdDbPool pool = RrdDbPool.getInstance();
/**
* @see org.jivesoftware.messenger.container.Plugin#initializePlugin(org.jivesoftware.messenger.container.PluginManager, java.io.File)
*/
public void initializePlugin(PluginManager manager, File pluginDirectory) {
this.manager = manager;
this.pluginDirectory = pluginDirectory;
File store = new File(pluginDirectory, "store");
if(!store.exists()) {
store.mkdirs();
}
try {
rrdManager = new RrdManager(store.getCanonicalFile(), pool);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
preferences = Preferences.systemNodeForPackage(MeterPlugin.class);
String monitoredObjectNames = preferences.get("monitoredObjectNames", "java.lang:type=Threading;java.lang:type=Memory");
for(String name : monitoredObjectNames.split("; ?")) {
logger.log(Level.FINER, "initialize.objectname", name);
try {
ObjectName objectName = new ObjectName(name);
Accumulator accum = rrdManager.create(objectName, server.getMBeanInfo(objectName));
/**
* Schedule the accumulator.
*/
executors.scheduleAtFixedRate(accum, 0, 300, TimeUnit.SECONDS);
} catch (MalformedObjectNameException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NullPointerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstanceNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (RrdException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IntrospectionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ReflectionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* @see org.jivesoftware.messenger.container.Plugin#destroyPlugin()
*/
public void destroyPlugin() {
}
}
/**
*
*/
package org.jivesoftware.messenger.plugin.meter;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.naming.OperationNotSupportedException;
import org.apache.commons.jxpath.DynamicPropertyHandler;
/**
* @author Noah Campbell
* @version 1.0
*/
public class ObjectNamePropertyHandler implements DynamicPropertyHandler {
/** The server. */
private static final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
/** The logger. */
final static private Logger logger = Logger.getLogger(
"objectnamepropertyhandler", "resources");
/**
* @see org.apache.commons.jxpath.DynamicPropertyHandler#getPropertyNames(java.lang.Object)
*/
public String[] getPropertyNames(Object object) {
ArrayList<String> list = new ArrayList<String>();
try {
ObjectName objectName = (ObjectName)object;
MBeanInfo info = server.getMBeanInfo(objectName);
MBeanAttributeInfo[] attrs = info.getAttributes();
for(MBeanAttributeInfo attr : attrs) {
list.add(attr.getName());
}
} catch (Exception e) {
logger.log(Level.WARNING, "unabletoevaluatepropertyname", e);
}
return list.toArray(new String[list.size()]);
}
/**
* @see org.apache.commons.jxpath.DynamicPropertyHandler#getProperty(java.lang.Object, java.lang.String)
*/
public Object getProperty(Object object, String propertyName) {
try {
return server.getAttribute((ObjectName) object, propertyName);
} catch (AttributeNotFoundException e) {
logger.log(Level.WARNING, "attributenotfound", e);
} catch (InstanceNotFoundException e) {
logger.log(Level.WARNING, "instancenotfound", e);
} catch (MBeanException e) {
logger.log(Level.WARNING, "mbeanexception", e);
} catch (ReflectionException e) {
logger.log(Level.WARNING, "reflectionexception", e);
}
return null;
}
/**
* @see org.apache.commons.jxpath.DynamicPropertyHandler#setProperty(java.lang.Object, java.lang.String, java.lang.Object)
*/
public void setProperty(@SuppressWarnings("unused") Object object,
@SuppressWarnings("unused") String propertyName,
@SuppressWarnings("unused") Object value) {
throw new RuntimeException(new OperationNotSupportedException("unable to perform this opertion."));
}
}
/**
*
*/
package org.jivesoftware.messenger.plugin.meter;
import org.jrobin.annotations.Ds;
import org.jrobin.annotations.Rrd;
/**
* @author Noah Campbell
* @version 1.0
*/
@Rrd
public interface ThreadMXBeanSupportInfo {
/**
* @return time The current thread cpu time.
*/
@Ds(name="cputime", expr="/CurrentThreadCpuTime" )
long getCurrentThreadCpuTime();
/**
* @return count The current thread count.
*/
@Ds(name="threadCount", expr="/ThreadCount")
long getThreadCount();
}
/**
*
*/
package org.jivesoftware.messenger.plugin.meter;
/**
* @author Noah Campbell
* @version 1.0
*/
public class UnableToAllocateAccumulator extends Exception {
/** The serialVersionUID. */
private static final long serialVersionUID = 1L;
/**
* Construct a new <code>UnableToAllocateAccumulator</code>.
*
*/
public UnableToAllocateAccumulator() {
super();
// TODO Auto-generated constructor stub
}
/**
* Construct a new <code>UnableToAllocateAccumulator</code>.
*
* @param message
* @param cause
*/
public UnableToAllocateAccumulator(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
/**
* Construct a new <code>UnableToAllocateAccumulator</code>.
*
* @param message
*/
public UnableToAllocateAccumulator(String message) {
super(message);
// TODO Auto-generated constructor stub
}
/**
* Construct a new <code>UnableToAllocateAccumulator</code>.
*
* @param cause
*/
public UnableToAllocateAccumulator(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
/**
*
*/
package org.jrobin.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Noah Campbell
* @version 1.0
*/
@Target(value=ElementType.TYPE)
@Retention(value=RetentionPolicy.RUNTIME)
public @interface Arc {
/**
* The data is also consolidated with the consolidation function (CF) of the
* archive. The following consolidation functions are defined:
* AVERAGE, MIN, MAX, LAST.
* @return confun The standard consolidation functions (AVERAGE, MIN, MAX, LAST) for the archive.
*/
ConsolidationFunction consolidationFunction();
/**
* xff The xfiles factor defines what part of a consolidation interval may
* be made up from *UNKNOWN* data while the consolidated value is still
* regarded as known.
*
* @return xff
*/
double xff();
/**
* steps defines how many of these primary data points are used to build a
* consolidated data point which then goes into the archive.
* @return steps
*/
int steps();
/**
* rows defines how many generations of data values are kept in an RRA.
* @return rows
*/
int rows();
}
/**
*
*/
package org.jrobin.annotations;
/**
* @author Noah Campbell
* @version 1.0
*/
public enum ConsolidationFunction {
/** The AVERAGE. */
AVERAGE,
/** The MIN. */
MIN,
/** The MAX. */
MAX,
/** The LAST. */
LAST
}
/**
*
*/
package org.jrobin.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Noah Campbell
* @version 1.0
*/
@Retention(value=RetentionPolicy.RUNTIME)
@Target(value={ElementType.METHOD})
public @interface Ds {
/**
* Name is the name you will use to reference this particular data source
* from an RRD. A ds-name must be 1 to 19 characters long in the
* characters [a-zA-Z0-9_].
* @return name
*/
String name();
/**
* <em>type</em> defines the Data Source Type. See the section on
* ``How to Measure'' below for further insight. The Datasource Type must
* be one of the following:</p>
* <dl>
*
* @return sourceType
*/
SourceType type() default SourceType.COUNTER;
/**
* heartbeat defines the maximum number of seconds that may pass between two
* updates of this data source before the value of the data source is
* assumed to be *UNKNOWN*.
*/
long heartbeat() default 600;
/**
* min and max are optional entries defining the expected range of the data
* supplied by this data source. If min and/or max are defined, any value
* outside the defined range will be regarded as *UNKNOWN*. If you do not
* know or care about min and max, set them to U for unknown.
* Note that min and max always refer to the processed values of the DS.
* For a traffic-COUNTER type DS this would be the max and min data-rate
* expected from the device.
*/
double minValue() default Double.MIN_VALUE;
/**
* @see #minValue()
* @return maxValue
*/
double maxValue() default Double.MAX_VALUE;
/**
* A XPath expression that defines how the data is collected from the object
* model. This is bean notion with a few exceptions. The attributes from
* the mbean server are upper case for the first character (as opposed to
* first letter lower case. Composite data are the exact string value as
* they are stored.
*
* For example: MBeanAttribute/composite/foo/bar/baz would lookup the attribute
* named MBeanAttribute (invoking the getAttribute on the server). Composite
* would call CompositeData.getField(<i>literal</i>). If the field is an object
* than the object graph is traversed with the rules defined in JXPath.
*
* The result should always be a number (short, long, double, float and the
* appropriate Object types). If it's not a number, 0 is returned.
*
* If no expression is given, then it's assumed that the name of the data source
* is the attribute on the ObjectName that it references.
*
* @see "JXPath"
* @return expression
*/
String expr() default "";
}
/**
*
*/
package org.jrobin.annotations;
/**
* Marker interface for Rrd#override()
*
* @author Noah Campbell
* @version 1.0
*/
public interface None {
}
/**
*
*/
package org.jrobin.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Noah Campbell
* @version 1.0
*/
@Target(value=ElementType.TYPE)
@Retention(value=RetentionPolicy.RUNTIME)
public @interface Rrd {
/**
* RRD gets fed samples at arbitrary times. From these it builds Primary
* Data Points (PDPs) at exact times every ``step'' interval. The PDPs are
* then accumulated into RRAs.
*
* <b>Note:</b> Changing this value from its default of 300 will impact the
* storage of the default archives.
*
* @return steps Number of seconds between expected reads.
*/
long step() default 300;
/**
* The class that this Rrd overrides.
* @return cls
*/
Class override() default None.class;
/**
* Archives to be associated with this rrd, beyond the Ds defined.
* @return archives The standard type of archives.
*
* This will be explained later on. The time in-between samples is 300
* seconds, a good starting point, which is the same as five minutes.
*
* 1 sample "averaged" stays 1 period of 5 minutes
* 6 samples averaged become one average on 30 minutes
* 24 samples averaged become one average on 2 hours
* 288 samples averaged become one average on 1 day
*
* <b>Note:</b> Changing the default step will have an impact on the archives that are
* created.
*/
Arc[] archives() default {@Arc(consolidationFunction=ConsolidationFunction.AVERAGE, xff=0.5, steps=1, rows=600),
@Arc(consolidationFunction=ConsolidationFunction.AVERAGE, xff=0.5, steps=5, rows=700),
@Arc(consolidationFunction=ConsolidationFunction.AVERAGE, xff=0.5, steps=24, rows=775),
@Arc(consolidationFunction=ConsolidationFunction.AVERAGE, xff=0.5, steps=288, rows=797),
@Arc(consolidationFunction=ConsolidationFunction.MAX, xff=0.5, steps=1, rows=600),
@Arc(consolidationFunction=ConsolidationFunction.MAX, xff=0.5, steps=5, rows=700),
@Arc(consolidationFunction=ConsolidationFunction.MAX, xff=0.5, steps=24, rows=775),
@Arc(consolidationFunction=ConsolidationFunction.MAX, xff=0.5, steps=288, rows=797),
@Arc(consolidationFunction=ConsolidationFunction.MIN, xff=0.5, steps=1, rows=600),
@Arc(consolidationFunction=ConsolidationFunction.MIN, xff=0.5, steps=5, rows=700),
@Arc(consolidationFunction=ConsolidationFunction.MIN, xff=0.5, steps=24, rows=775),
@Arc(consolidationFunction=ConsolidationFunction.MIN, xff=0.5, steps=288, rows=797)};
}
/**
*
*/
package org.jrobin.annotations;
/**
* @author Noah Campbell
* @version 1.0
*/
public enum SourceType {
/**
* </dd><dt><strong><a name="item_COUNTER"><strong>COUNTER</strong></a></strong><br>
* </dt><dd>is for continuous incrementing counters like the InOctets counter in a router. The <strong>COUNTER</strong>
* data source assumes that the counter never decreases, except when a
* counter overflows. The update function takes the overflow into account.
* The counter is stored as a per-second rate. When the counter overflows,
* RRDtool checks if the overflow happened at the 32bit or 64bit border
* and acts accordingly by adding an appropriate value to the result. <p></p>
*/
COUNTER,
/**
* <dt><strong><a name="item_GAUGE"><strong>GAUGE</strong></a></strong><br>
* </dt><dd>is for things like temperatures or number of people in a room or value of a RedHat share.
* <p></p>
*/
GAUGE,
/**
* </dd><dt><strong><a name="item_DERIVE"><strong>DERIVE</strong></a></strong><br>
* </dt><dd>will store the derivative of the line going from the last to
* the current value of the data source. This can be useful for gauges,
* for example, to measure the rate of people entering or leaving a room.
* Internally, derive works exaclty like COUNTER but without overflow
* checks. So if your counter does not reset at 32 or 64 bit you might
* want to use DERIVE and combine it with a MIN value of 0. <p></p>
*/
DERIVE,
/**
* </dd><dt><strong><a name="item_ABSOLUTE"><strong>ABSOLUTE</strong></a></strong><br>
* </dt><dd>is for counters which get reset upon reading. This is used for
* fast counters which tend to overflow. So instead of reading them
* normally you reset them after every read to make sure you have a
* maximal time available before the next overflow. Another usage is for
* things you count like number of messages since the last update.
*/
ABSOLUTE;
}
<?xml version='1.0' encoding='ISO-8859-1'?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<!-- Servlets -->
<servlet>
<display>JRobin Graphing Servlet</display>
<servlet-name>GraphServlet</servlet-name>
<servlet-class>org.jivesoftware.messenger.plugin.monitor.GraphServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<display-name>DWR Servlet</display-name>
<description>Direct Web Remoter Servlet</description>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Servlet mappings -->
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>GraphServlet</servlet-name>
<url-pattern>/chart.pngx</url-pattern>
</servlet-mapping>
</web-app>
<%@ page import="java.util.*,
org.jivesoftware.admin.*,
org.jivesoftware.messenger.XMPPServer,
org.jivesoftware.util.*"
errorPage="error.jsp"
%>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
<%-- Define Administration Bean --%>
<jsp:useBean id="admin" class="org.jivesoftware.util.WebManager" />
<c:set var="admin" value="${admin.manager}" />
<% admin.init(request, response, session, application, out ); %>
<jsp:useBean id="pageinfo" scope="request" class="org.jivesoftware.admin.AdminPageBean" />
<%
String title = "Core Monitor Graphing Properties";
pageinfo.setTitle(title);
pageinfo.getBreadcrumbs().add(new AdminPageBean.Breadcrumb(LocaleUtils.getLocalizedString("global.main"), "../../index.jsp"));
pageinfo.getBreadcrumbs().add(new AdminPageBean.Breadcrumb(title, "graphsetup.jsp"));
pageinfo.setPageID("monitor-service");
%>
<jsp:include page="top.jsp" flush="true" />
<jsp:include page="title.jsp" flush="true" />
<jsp:useBean id="graphBuilder" scope="session" class="org.jivesoftware.messenger.plugin.monitor.GraphBuilderBean"/>
<div style="width:100%;border:1px solid black;">
<img id="img" src="/plugins/monitor/chart?store=store">
<div>
<link rel="stylesheet" type="text/css" href="http://www.jivesoftware.org/forums/style.jsp">
<style>
table {border-collapse:collapse;}
table td {padding:3px;}
caption {font-weight:bold;background:#5500FF;}
fieldset {margin:5px;width:750px;}
table {width:100%;}
input[type="button"] {width:65px;}
td input[type="button"] {text-align:right;}
input[type="text"] {border:black solid 1px;}
select {border:black solid 1px;}
tr.hover {background: gray;}
td.action {text-align:right;}
td>input[type="text"] {width:100%;}
td>select {width:95%;}
tfoot td {text-align:center;}
tfoot>tr:hover {width:65px;background:#AAAAAA;}
tfoot {visibility:hidden;}
thead td {font-weight:bold;}
</style>
<script type="text/javascript" src="dwr/utils.js"></script>
<script type="text/javascript" src="dwr/engine.js"></script>
<script>
function toggle(id) {
var style = id.style.visibility;
if(style == 'hidden' || style == '')
id.style.visibility='visible';
else
id.style.visibility='hidden';
}
</script>
<form ACTION="graphsetup.jsp" method="GET">
<fieldset>
<table width="100%" border=0>
<tbody>
<tr>
<td>ObjectName</td>
<td><select>
<c:forEach var="mbean" items="${graphBuilder.chartableMBeans}">
<option><c:out value="${mbean}" /></option>
</c:forEach>
</select></td>
<td>Store</td>
<td><select><c:forEach var="store" items="${graphBuilder.stores}">
<option><c:out value="${store}"/></option>
</c:forEach></select></td>
</tr>
</tbody>
</table>
</fieldset>
<fieldset>
<table width="100%">
<caption>Datasources</caption>
<thead>
<tr>
<td>Graph Item</td>
<td>DataSource</td>
<td>Name</td>
<td>CF Type</td>
<td class="action"><input type="button" value="add"></td>
</tr>
</thead>
<tbody>
<!-- sample data -->
<tr>
<td>input0</td>
<td>somefilename.rrd</td>
<td>in</td>
<td>AVERAGE</td>
<td class="action"><input type="button" value="edit"></td>
<tr>
<!-- sample expr -->
<tr>
<td>input1</td>
<td colspan="3">in,8,+</td>
<td class="action"><input type="button" value="edit"></td>
</tr>
</tbody>
<tfoot>
<tr>
<td><input type="text" name="name"></td>
<td><input type="text" name="name"></td>
<td><input type="text" name="name"></td>
<td><select name="confunc">
<option>AVERAGE</option>
<option>MAX</option>
<option>MIN</option>
<option>LAST</option>
</select>
</td>
<td class="action"><input type="button" value="update"></td>
</tr>
</tfoot>
</table>
</fieldset>
<fieldset>
<table width="100%">
<caption>Graph Elements</caption>
<thead>
<tr>
<td>Type</td>
<td>Datasource</td>
<td>Color</td>
<td>Title</td>
<td class="action"><input type="button" value="add"></td>
</tr>
</thead>
<tbody>
<!-- sample graph element -->
<tr>
<td>LINE</td>
<td>in8</td>
<td>#AA333344</td>
<td>Input</td>
<td class="action"><input type="button" value="edit"></td>
</tr>
</tr>
</tbody>
<tfoot>
<tr>
<td><select name="type">
<option>LINE</option>
<option>AREA</option>
<option>STACK</option>
<option>HRULE</option>
<option>VRULE</option>
</select>
</td>
<td><input type="text" value=""></td>
<td><input type="text" value=""></td>
<td><input type="text" value=""></td>
<td class="action"><input type="button" value="edit"></td>
</tr>
</tfoot>
</table>
</fieldset>
<fieldset>
<table width="100%">
<caption>Legend</caption>
<thead>
<tr>
<td>Name</td>
<td>Consol Function</td>
<td>Description</td>
<td class="action"><input type="button" value="add" onClick="toggle(graphedit)"></td>
</tr>
</tr>
</thead>
<tbody>
<tr>
<td>in8</td>
<td>AVERAGE</td>
<td>avgTotal=%.2lf %sbits/sec</td>
<td class="action"><input type="button" value="edit"></td>
</tr>
</tbody>
<tfoot id="graphedit">
<tr>
<td><input type="text"></td>
<td><select name="confunc">
<option>AVERAGE</option>
<option>MAX</option>
<option>MIN</option>
<option>LAST</option>
</select>
</td>
<td><input type="text"></td>
<td class="action"><input value="update" type="button"></td>
</tr>
</tfoot>
</table
</fieldset>
</form>
\ 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