Author: rmannibucau Date: Thu Aug 1 08:32:00 2013 New Revision: 1509112 URL: http://svn.apache.org/r1509112 Log: escaping method parameter by parameter for jmx method invocation + fixing JMX names of counters
Modified: commons/sandbox/monitoring/trunk/core/src/main/java/org/apache/commons/monitoring/counter/factory/DefaultCounterFactory.java commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jmx/JMXHandler.java commons/sandbox/monitoring/trunk/reporting/src/main/resources/templates/jmx/mbean.vm Modified: commons/sandbox/monitoring/trunk/core/src/main/java/org/apache/commons/monitoring/counter/factory/DefaultCounterFactory.java URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/core/src/main/java/org/apache/commons/monitoring/counter/factory/DefaultCounterFactory.java?rev=1509112&r1=1509111&r2=1509112&view=diff ============================================================================== --- commons/sandbox/monitoring/trunk/core/src/main/java/org/apache/commons/monitoring/counter/factory/DefaultCounterFactory.java (original) +++ commons/sandbox/monitoring/trunk/core/src/main/java/org/apache/commons/monitoring/counter/factory/DefaultCounterFactory.java Thu Aug 1 08:32:00 2013 @@ -21,19 +21,34 @@ import org.apache.commons.monitoring.Rol import org.apache.commons.monitoring.configuration.Configuration; import org.apache.commons.monitoring.counter.Counter; import org.apache.commons.monitoring.counter.DefaultCounter; -import org.apache.commons.monitoring.util.ClassLoaders; +import org.apache.commons.monitoring.monitors.Monitor; +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.DynamicMBean; +import javax.management.InvalidAttributeValueException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanConstructorInfo; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanOperationInfo; import javax.management.MBeanServer; import javax.management.ObjectName; +import javax.management.ReflectionException; import java.io.Closeable; import java.io.IOException; import java.lang.management.ManagementFactory; -import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; -import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; public class DefaultCounterFactory implements CounterFactory, Closeable { private static final boolean JMX = Configuration.isActivated(Configuration.Keys.JMX); @@ -53,14 +68,16 @@ public class DefaultCounterFactory imple } try { - final ObjectName name = new ObjectName("org.apache.commons.monitoring:type=counter,name=" + counter.getMonitor().getKey().getName()); - SERVER.registerMBean(CounterMBean.class.cast(Proxy.newProxyInstance(ClassLoaders.current(), new Class<?>[] { CounterMBean.class }, new InvocationHandler() { - @Override - public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { - return method.invoke(counter, args); - } - })), name); - toUnregister.add(name); + final ObjectName name = new ObjectName( + "org.apache.commons.monitoring:" + + "type=counter," + + "monitorName=" + counter.getMonitor().getKey().getName().replace(",", " ").replace("=", " ").replace(";", " ").replace("*", " ").replace("?", " ") + "," + + "monitorCategory=" + counter.getMonitor().getKey().getCategory() + "," + + "role=" + counter.getRole().getName()); + if (!SERVER.isRegistered(name)) { + SERVER.registerMBean(new CounterMBean(counter), name); + toUnregister.add(name); + } } catch (final Exception e) { throw new MonitoringException(e); } @@ -77,6 +94,114 @@ public class DefaultCounterFactory imple } } - public static interface CounterMBean extends Counter { + public static class CounterMBean implements DynamicMBean { + private static final Map<String, Method> METHODS = new ConcurrentHashMap<String, Method>(); + private static final List<String> VIRTUAL_FIELDS = new CopyOnWriteArrayList<String>() {{ + add("Role"); + add("Monitor"); + add("Unit"); + }}; + private static final MBeanInfo INFO = computeCounterMBeanInfo(); + + private static MBeanInfo computeCounterMBeanInfo() { + final Collection<MBeanAttributeInfo> attributes = new LinkedList<MBeanAttributeInfo>(); + final Collection<MBeanOperationInfo> operations = new LinkedList<MBeanOperationInfo>(); + + for (final Method m : Counter.class.getMethods()) { + final String name = m.getName(); + if ("setMonitor".equals(name)) { + continue; + } + + if (name.startsWith("get")) { + final String attributeName = name.substring("get".length()); + if (VIRTUAL_FIELDS.contains(attributeName)) { + attributes.add(new MBeanAttributeInfo(attributeName, String.class.getName(), attributeName + " value", true, false, false)); + } else { + attributes.add(new MBeanAttributeInfo(attributeName, m.getReturnType().getName(), attributeName + " value", true, false, false)); + } + } else { + METHODS.put(name, m); + operations.add(new MBeanOperationInfo(name + " method", m)); + } + } + + return new MBeanInfo( + CounterMBean.class.getName(), + "Counter MBean", + attributes.toArray(new MBeanAttributeInfo[attributes.size()]), + new MBeanConstructorInfo[0], + operations.toArray(new MBeanOperationInfo[operations.size()]), + new MBeanNotificationInfo[0]); + } + + private final Counter delegate; + + public CounterMBean(final Counter counter) { + delegate = counter; + } + + @Override + public Object getAttribute(final String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException { + if (VIRTUAL_FIELDS.contains(attribute)) { + if ("Role".equals(attribute)) { + final Role role = delegate.getRole(); + return role.getName() + " (" + role.getUnit().getName() + ")"; + } + if ("Unit".equals(attribute)) { + return delegate.getUnit().getName(); + } + if ("Monitor".equals(attribute)) { + final Monitor.Key key = delegate.getMonitor().getKey(); + return key.getName() + " (" + key.getCategory() + ")"; + } + } + try { + return Counter.class.getMethod("get" + attribute).invoke(delegate); + } catch (final Exception e) { + throw new AttributeNotFoundException(e.getMessage()); + } + } + + @Override + public void setAttribute(final Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { + throw new UnsupportedOperationException("Read Only MBean"); + } + + @Override + public AttributeList getAttributes(final String[] attributes) { + final AttributeList list = new AttributeList(); + for (final String attribute : attributes) { + try { + list.add(new Attribute(attribute, getAttribute(attribute))); + } catch (final Exception e) { + // no-op + } + } + return list; + } + + @Override + public AttributeList setAttributes(final AttributeList attributes) { + throw new UnsupportedOperationException("Read Only MBean"); + } + + @Override + public Object invoke(final String actionName, final Object[] params, final String[] signature) throws MBeanException, ReflectionException { + final Method m = METHODS.get(actionName); + if (m == null) { + throw new MBeanException(new NullPointerException(), "method " + actionName + " doesn't exist"); + } + try { + return m.invoke(delegate, params); + } catch (final Exception e) { + throw new MBeanException(e, e.getMessage()); + } + } + + @Override + public MBeanInfo getMBeanInfo() { + return INFO; + } } } Modified: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jmx/JMXHandler.java URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jmx/JMXHandler.java?rev=1509112&r1=1509111&r2=1509112&view=diff ============================================================================== --- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jmx/JMXHandler.java (original) +++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jmx/JMXHandler.java Thu Aug 1 08:32:00 2013 @@ -18,6 +18,7 @@ package org.apache.commons.monitoring.re import org.apache.commons.codec.binary.Base64; import org.apache.commons.monitoring.MonitoringException; +import org.apache.commons.monitoring.configuration.Configuration; import org.apache.commons.monitoring.reporting.web.handler.HandlerRendererAdapter; import org.apache.commons.monitoring.reporting.web.handler.Renderer; import org.apache.commons.monitoring.reporting.web.template.MapBuilder; @@ -35,9 +36,11 @@ import javax.management.ObjectName; import javax.management.openmbean.CompositeData; import javax.management.openmbean.TabularData; import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; import java.lang.management.ManagementFactory; import java.lang.reflect.Array; import java.lang.reflect.Field; +import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -50,6 +53,7 @@ import java.util.Set; public class JMXHandler extends HandlerRendererAdapter { private static final MBeanServer SERVER = ManagementFactory.getPlatformMBeanServer(); + private static final boolean METHOD_INVOCATION_ALLOWED = Configuration.is(Configuration.COMMONS_MONITORING_PREFIX + "jmx.method.allowed", true); private static final Map<String, Class<?>> WRAPPERS = new HashMap<String, Class<?>>(); @@ -74,19 +78,31 @@ public class JMXHandler extends HandlerR String subPath = path.substring("/jmx/".length()); if (subPath.startsWith("operation/")) { + if (!METHOD_INVOCATION_ALLOWED) { + throw new MonitoringException("Method invocation not allowed"); + } + subPath = subPath.substring("operation/".length()); final String[] parts = subPath.split("/"); - - final Collection<String> params = new ArrayList<String>(parts.length - 2); + final List<String> params = new ArrayList<String>(parts.length - 2); { // remove object name and operation name to keep only parameters params.addAll(Arrays.asList(parts)); final Iterator<String> it = params.iterator(); it.next(); it.remove(); it.next(); it.remove(); } + // decode params + final String[] decodedParams = new String[params.size()]; + for (int i = 0; i < params.size(); i++) { + try { + decodedParams[i] = URLDecoder.decode(params.get(i), "UTF-8"); + } catch (final UnsupportedEncodingException e) { + decodedParams[i] = params.get(i); + } + } try { - return new InvokeRenderer(new ObjectName(new String(Base64.decodeBase64(parts[0]))), parts[1], params.toArray(new String[params.size()])); + return new InvokeRenderer(new ObjectName(new String(Base64.decodeBase64(parts[0]))), parts[1], decodedParams); } catch (final MalformedObjectNameException e) { throw new MonitoringException(e); } Modified: commons/sandbox/monitoring/trunk/reporting/src/main/resources/templates/jmx/mbean.vm URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/resources/templates/jmx/mbean.vm?rev=1509112&r1=1509111&r2=1509112&view=diff ============================================================================== --- commons/sandbox/monitoring/trunk/reporting/src/main/resources/templates/jmx/mbean.vm (original) +++ commons/sandbox/monitoring/trunk/reporting/src/main/resources/templates/jmx/mbean.vm Thu Aug 1 08:32:00 2013 @@ -76,7 +76,7 @@ // we pass base64 as id so this is a workaround since = will likely make #id fail $('[id=\'' + id + '\']' + ' :input').each(function () { - url = url + "/" + $(this).val(); + url = url + "/" + encodeURIComponent($(this).val()); }); $.get(url, function(data) {