You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by rm...@apache.org on 2013/08/05 10:48:05 UTC

svn commit: r1510386 [1/2] - in /commons/sandbox/monitoring/trunk: core/src/main/java/org/apache/commons/monitoring/ reporting/src/main/java/org/apache/commons/monitoring/reporting/web/ reporting/src/main/java/org/apache/commons/monitoring/reporting/we...

Author: rmannibucau
Date: Mon Aug  5 08:48:04 2013
New Revision: 1510386

URL: http://svn.apache.org/r1510386
Log:
reworking plugin API, in particular getting rid of Handler/Renderer to use something closer to REST (@Regex)

Added:
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/FilteringEndpoints.java
      - copied, changed from r1510370, commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/FilteringHandler.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/HomeEndpoint.java
      - copied, changed from r1510370, commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/HomeHandler.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/Regex.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/Template.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/TemplateHelper.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/internal/
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/internal/EndpointInfo.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/internal/Invoker.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jmx/JMXEndpoints.java
      - copied, changed from r1510370, 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/java/org/apache/commons/monitoring/reporting/web/plugin/jvm/JVMEndpoints.java
      - copied, changed from r1510370, commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jvm/JVMHandler.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ReportEndpoints.java
      - copied, changed from r1510370, commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ReportHandler.java
    commons/sandbox/monitoring/trunk/reporting/src/main/resources/templates/report/report-csv.vm
    commons/sandbox/monitoring/trunk/reporting/src/main/resources/templates/report/report-json.vm
    commons/sandbox/monitoring/trunk/reporting/src/main/resources/templates/report/report-xml.vm
      - copied, changed from r1510370, commons/sandbox/monitoring/trunk/reporting/src/main/resources/templates/report/report.vm
Removed:
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/FilteringHandler.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/Handler.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/HandlerRendererAdapter.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/HomeHandler.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/RedirectHandler.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/Renderer.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/PluginDecoratorHandler.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/java/org/apache/commons/monitoring/reporting/web/plugin/jvm/JVMHandler.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ClearHandler.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/FormatReportHandler.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ReportHandler.java
Modified:
    commons/sandbox/monitoring/trunk/core/src/main/java/org/apache/commons/monitoring/MonitoringException.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/MonitoringController.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/Plugin.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/PluginRepository.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jmx/JMXPlugin.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jvm/JVMPlugin.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ReportPlugin.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/CSVFormat.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/Format.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/HTMLFormat.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/JSONFormat.java
    commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/XMLFormat.java
    commons/sandbox/monitoring/trunk/reporting/src/main/resources/templates/home.vm
    commons/sandbox/monitoring/trunk/reporting/src/main/resources/templates/jvm/jvm.vm
    commons/sandbox/monitoring/trunk/reporting/src/main/resources/templates/page.vm
    commons/sandbox/monitoring/trunk/reporting/src/test/java/org/apache/commons/monitoring/reporting/FormatsTest.java

Modified: commons/sandbox/monitoring/trunk/core/src/main/java/org/apache/commons/monitoring/MonitoringException.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/core/src/main/java/org/apache/commons/monitoring/MonitoringException.java?rev=1510386&r1=1510385&r2=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/core/src/main/java/org/apache/commons/monitoring/MonitoringException.java (original)
+++ commons/sandbox/monitoring/trunk/core/src/main/java/org/apache/commons/monitoring/MonitoringException.java Mon Aug  5 08:48:04 2013
@@ -17,7 +17,7 @@
 package org.apache.commons.monitoring;
 
 public class MonitoringException extends RuntimeException {
-    public MonitoringException(final Exception e) {
+    public MonitoringException(final Throwable e) {
         super(e);
     }
 

Modified: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/MonitoringController.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/MonitoringController.java?rev=1510386&r1=1510385&r2=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/MonitoringController.java (original)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/MonitoringController.java Mon Aug  5 08:48:04 2013
@@ -16,10 +16,10 @@
  */
 package org.apache.commons.monitoring.reporting.web;
 
-import org.apache.commons.monitoring.reporting.web.handler.FilteringHandler;
-import org.apache.commons.monitoring.reporting.web.handler.Handler;
-import org.apache.commons.monitoring.reporting.web.handler.HomeHandler;
-import org.apache.commons.monitoring.reporting.web.handler.Renderer;
+import org.apache.commons.monitoring.reporting.web.handler.FilteringEndpoints;
+import org.apache.commons.monitoring.reporting.web.handler.HomeEndpoint;
+import org.apache.commons.monitoring.reporting.web.handler.internal.EndpointInfo;
+import org.apache.commons.monitoring.reporting.web.handler.internal.Invoker;
 import org.apache.commons.monitoring.reporting.web.plugin.PluginRepository;
 import org.apache.commons.monitoring.reporting.web.template.MapBuilder;
 import org.apache.commons.monitoring.reporting.web.template.Templates;
@@ -32,19 +32,27 @@ import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 public class MonitoringController implements Filter {
     private final Map<String, byte[]> cachedResources = new HashMap<String, byte[]>();
-    private final Map<String, Handler> handlers = new HashMap<String, Handler>();
-    private Handler defaultHandler;
+    private final Map<Pattern, Invoker> invokers = new HashMap<Pattern, Invoker>();
     private String mapping;
     private ClassLoader classloader;
+    private Invoker defaultInvoker;
 
     @Override
     public void init(final FilterConfig config) throws ServletException {
@@ -55,18 +63,17 @@ public class MonitoringController implem
     }
 
     private void initHandlers() {
-        defaultHandler = new HomeHandler();
-        handlers.put("/", defaultHandler);
-        handlers.put("/home", defaultHandler);
+        // home page
+        invokers.putAll(EndpointInfo.build(HomeEndpoint.class, null, "").getInvokers());
+        defaultInvoker = invokers.values().iterator().next();
 
         // filtered to get the right base for pictures
-        handlers.put("/resources/css/monitoring.css", FilteringHandler.INSTANCE);
-        handlers.put("/resources/css/bootstrap.min.css", FilteringHandler.INSTANCE);
+        invokers.putAll(EndpointInfo.build(FilteringEndpoints.class, null, "").getInvokers());
 
-        // plugins handlers
+        // plugins
         for (final PluginRepository.PluginInfo plugin : PluginRepository.PLUGIN_INFO) {
-            if (plugin.getHandler() != null && plugin.getUrl() != null) {
-                handlers.put("/" + plugin.getUrl(), plugin.getHandler());
+            for (final Map.Entry<Pattern, Invoker> invoker : plugin.getInvokers().entrySet()) {
+                invokers.put(invoker.getKey(), invoker.getValue());
             }
         }
     }
@@ -91,17 +98,46 @@ public class MonitoringController implem
         }
 
         final HttpServletRequest httpRequest = HttpServletRequest.class.cast(request);
+        final HttpServletResponse httpResponse = HttpServletResponse.class.cast(response);
 
         String path = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length() + mapping.length());
         if (!path.startsWith("/")) {
             path = "/" + path;
         }
 
-        final Handler handler = findHandler(path);
-        if (handler == defaultHandler && !"/".equals(path)){ // resource, they are in the classloader and not in the webapp for embedded case
+        // find the matching invoker
+        Invoker invoker = defaultInvoker;
+        Matcher matcher = null;
+        for (final Map.Entry<Pattern, Invoker> entry : invokers.entrySet()) {
+            matcher = entry.getKey().matcher(path);
+            if (matcher.matches()) {
+                invoker = entry.getValue();
+                break;
+            }
+        }
+
+        // resource, they are in the classloader and not in the webapp to ease the embedded case
+        if (path.startsWith("/resources/")) {
             byte[] bytes = cachedResources.get(path);
             if (bytes == null) {
-                final InputStream is = classloader.getResourceAsStream(path.substring(1));
+                final InputStream is;
+                if (invoker != defaultInvoker) { // resource is filtered so filtering it before caching it
+                    final StringWriter writer = new StringWriter();
+                    final PrintWriter printWriter = new PrintWriter(writer);
+                    invoker.invoke(httpRequest, HttpServletResponse.class.cast(Proxy.newProxyInstance(classloader, new Class<?>[] { HttpServletResponse.class }, new InvocationHandler() {
+                        @Override
+                        public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+                            if ("getWriter".equals(method.getName())) {
+                                return printWriter;
+                            }
+                            return method.invoke(httpResponse, args);
+                        }
+                    })), null);
+                    is = new ByteArrayInputStream(writer.toString().getBytes());
+                } else {
+                    is = classloader.getResourceAsStream(path.substring(1));
+                }
+
                 if (is != null) {
                     final ByteArrayOutputStream baos = new ByteArrayOutputStream();
                     int i;
@@ -114,43 +150,37 @@ public class MonitoringController implem
                 }
             }
             if (bytes != null) {
-                response.getOutputStream().write(bytes);
+                httpResponse.getOutputStream().write(bytes);
                 return;
             }
         }
 
-        try {
-            final Renderer renderer = handler.handle(httpRequest, HttpServletResponse.class.cast(response), path);
-            if (renderer != null) {
-                renderer.render(response.getWriter(), request.getParameterMap());
+        // delegate handling to the invoker if request is not a resource
+        if (invoker == null) {
+            error(response, null);
+        } else {
+            try {
+                invoker.invoke(httpRequest, httpResponse, matcher);
+            } catch (final Exception e) {
+                error(response, e);
             }
-        } catch (final Exception e) {
-            final ByteArrayOutputStream err = new ByteArrayOutputStream();
-            e.printStackTrace(new PrintStream(err));
-            Templates.htmlRender(response.getWriter(), "error.vm", new MapBuilder<String, Object>().set("exception", new String(err.toByteArray())).build());
         }
     }
 
-    private Handler findHandler(final String path) {
-        final Handler handler = handlers.get(path);
-        if (handler != null) {
-            return handler;
-        }
-
-        for (final String mapping : handlers.keySet()) {
-            if (mapping.endsWith("/*") && path.startsWith(mapping.substring(0, mapping.length() - "/*".length()))) {
-                return handlers.get(mapping);
-            }
-            if (mapping.endsWith("*") && path.startsWith(mapping.substring(0, mapping.length() - "*".length()))) {
-                return handlers.get(mapping);
-            }
+    private void error(final ServletResponse response, final Exception e) throws IOException {
+        final String exception;
+        if (e != null) {
+            final ByteArrayOutputStream err = new ByteArrayOutputStream();
+            e.printStackTrace(new PrintStream(err));
+            exception = new String(err.toByteArray());
+        } else {
+            exception = "No matcher found";
         }
-
-        return defaultHandler;
+        Templates.htmlRender(response.getWriter(), "error.vm", new MapBuilder<String, Object>().set("exception", exception).build());
     }
 
     @Override
     public void destroy() {
-        handlers.clear();
+        invokers.clear();
     }
 }

Copied: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/FilteringEndpoints.java (from r1510370, commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/FilteringHandler.java)
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/FilteringEndpoints.java?p2=commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/FilteringEndpoints.java&p1=commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/FilteringHandler.java&r1=1510370&r2=1510386&rev=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/FilteringHandler.java (original)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/FilteringEndpoints.java Mon Aug  5 08:48:04 2013
@@ -16,36 +16,20 @@
  */
 package org.apache.commons.monitoring.reporting.web.handler;
 
-import org.apache.commons.monitoring.reporting.web.template.Templates;
+import org.apache.commons.monitoring.reporting.web.handler.api.Regex;
+import org.apache.commons.monitoring.reporting.web.handler.api.TemplateHelper;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.PrintWriter;
-import java.util.Collections;
-import java.util.Map;
-
-public class FilteringHandler implements Handler {
-    public static final FilteringHandler INSTANCE = new FilteringHandler();
-
-    private FilteringHandler() {
-        // no-op
-    }
-
-    @Override
-    public Renderer handle(final HttpServletRequest request, final HttpServletResponse response, final String path) {
-        return new FilteringRenderer(path);
+public class FilteringEndpoints {
+    private static final String BOOTSTRAP_CSS = "/resources/css/bootstrap.min.css";
+    private static final String MONITORING_CSS = "/resources/css/monitoring.css";
+
+    @Regex(MONITORING_CSS)
+    public void filterCss(final TemplateHelper helper) {
+        helper.renderPlain(MONITORING_CSS);
     }
 
-    private static class FilteringRenderer implements Renderer {
-        private final String path;
-
-        public FilteringRenderer(final String path) {
-            this.path = path;
-        }
-
-        @Override
-        public void render(final PrintWriter writer, final Map<String, ?> params) {
-            Templates.render(writer, path, Collections.<String, Object>emptyMap());
-        }
+    @Regex(BOOTSTRAP_CSS)
+    public void filterBootstrapCss(final TemplateHelper helper) {
+        helper.renderPlain(BOOTSTRAP_CSS);
     }
 }

Copied: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/HomeEndpoint.java (from r1510370, commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/HomeHandler.java)
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/HomeEndpoint.java?p2=commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/HomeEndpoint.java&p1=commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/HomeHandler.java&r1=1510370&r2=1510386&rev=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/HomeHandler.java (original)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/HomeEndpoint.java Mon Aug  5 08:48:04 2013
@@ -16,9 +16,12 @@
  */
 package org.apache.commons.monitoring.reporting.web.handler;
 
-public class HomeHandler extends HandlerRendererAdapter {
-    @Override
-    protected String getTemplate() {
-        return "home.vm";
+import org.apache.commons.monitoring.reporting.web.handler.api.Regex;
+import org.apache.commons.monitoring.reporting.web.handler.api.Template;
+
+public class HomeEndpoint {
+    @Regex
+    public Template home() {
+        return new Template("home.vm");
     }
 }

Added: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/Regex.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/Regex.java?rev=1510386&view=auto
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/Regex.java (added)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/Regex.java Mon Aug  5 08:48:04 2013
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.commons.monitoring.reporting.web.handler.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Regex {
+    String value() default ""; // root, the root page is mandatory, it is the one linked in the menu
+}

Added: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/Template.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/Template.java?rev=1510386&view=auto
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/Template.java (added)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/Template.java Mon Aug  5 08:48:04 2013
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.commons.monitoring.reporting.web.handler.api;
+
+import java.util.Collections;
+import java.util.Map;
+
+public class Template {
+    private final String template;
+    private final Map<String, ?> userParams;
+    private final boolean html;
+
+    public Template(final String template) {
+        this(template, Collections.<String, Object>emptyMap());
+    }
+
+    public Template(final String template, final Map<String, ?> userParams) {
+        this(template, userParams, true);
+    }
+
+    /**
+     * @param template template path, if isHtml is true it is relative to /templates otherwise it is absolute.
+     * @param userParams variables used by the template
+     * @param isHtml should the template be rendered with the site them or not (= is it a page fragment).
+     */
+    public Template(final String template, final Map<String, ?> userParams, final boolean isHtml) {
+        this.template = template;
+        this.userParams = userParams;
+        this.html = isHtml;
+    }
+
+    public String getTemplate() {
+        return template;
+    }
+
+    public Map<String, ?> getUserParams() {
+        return userParams;
+    }
+
+    public boolean isHtml() {
+        return html;
+    }
+}

Added: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/TemplateHelper.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/TemplateHelper.java?rev=1510386&view=auto
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/TemplateHelper.java (added)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/api/TemplateHelper.java Mon Aug  5 08:48:04 2013
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.commons.monitoring.reporting.web.handler.api;
+
+import org.apache.commons.monitoring.reporting.web.template.MapBuilder;
+import org.apache.commons.monitoring.reporting.web.template.Templates;
+
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.Map;
+
+public class TemplateHelper {
+    private final PrintWriter writer;
+    private final Map<String, ?> params;
+
+    public TemplateHelper(final PrintWriter writer, final Map<String, ?> params) {
+        this.writer = writer;
+        this.params = params;
+    }
+
+    public void renderHtml(final String template) {
+        renderHtml(template, Collections.<String, Object>emptyMap());
+    }
+
+    public void renderHtml(final String template, final Map<String, ?> userParams) {
+        Templates.htmlRender(writer, template, new MapBuilder<String, Object>().set(Map.class.cast(params)).set(Map.class.cast(userParams)).build());
+    }
+
+    public void renderPlain(final String template, final Map<String, ?> params) {
+        Templates.render(writer, template, params);
+    }
+
+    public void renderPlain(final String template) {
+        renderPlain(template, Collections.<String, Object>emptyMap());
+    }
+
+    public void write(final String message) {
+        writer.write(message);
+    }
+}

Added: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/internal/EndpointInfo.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/internal/EndpointInfo.java?rev=1510386&view=auto
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/internal/EndpointInfo.java (added)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/internal/EndpointInfo.java Mon Aug  5 08:48:04 2013
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.commons.monitoring.reporting.web.handler.internal;
+
+import org.apache.commons.monitoring.configuration.Configuration;
+import org.apache.commons.monitoring.reporting.web.handler.api.Regex;
+import org.apache.commons.monitoring.reporting.web.handler.api.TemplateHelper;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+public class EndpointInfo {
+    private final Map<Pattern, Invoker> invokers = new HashMap<Pattern, Invoker>();
+
+    public Map<Pattern, Invoker> getInvokers() {
+        return invokers;
+    }
+
+    public static EndpointInfo build(final Class<?> endpointClass, final String id, final String rootMapping) {
+        final EndpointInfo info = new EndpointInfo();
+        final Object instance = Configuration.newInstance(endpointClass);
+        for (final Method method : endpointClass.getMethods()) {
+            if (method.getDeclaringClass() == Object.class) {
+                continue;
+            }
+
+            final Regex regex = method.getAnnotation(Regex.class);
+            if (regex != null) {
+                final Pattern pattern = Pattern.compile(rootMapping + regex.value());
+                final Invoker invoker = new Invoker(instance, method, id);
+                int partIdx = 1; // regex index, it starts from 1
+                for (final Class<?> clazz : method.getParameterTypes()) {
+                    if (HttpServletRequest.class.equals(clazz)) {
+                        invoker.addRequestParameter();
+                    } else if (HttpServletResponse.class.equals(clazz)) {
+                        invoker.addResponseParameter();
+                    } else if (TemplateHelper.class.equals(clazz)) {
+                        invoker.addTemplateHelper(id);
+                    } else {
+                        invoker.addSegmentParameter(clazz, partIdx);
+                        partIdx++;
+                    }
+                }
+                info.invokers.put(pattern, invoker);
+            }
+        }
+        return info;
+    }
+}

Added: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/internal/Invoker.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/internal/Invoker.java?rev=1510386&view=auto
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/internal/Invoker.java (added)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/handler/internal/Invoker.java Mon Aug  5 08:48:04 2013
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.commons.monitoring.reporting.web.handler.internal;
+
+import org.apache.commons.monitoring.MonitoringException;
+import org.apache.commons.monitoring.reporting.web.handler.api.Template;
+import org.apache.commons.monitoring.reporting.web.handler.api.TemplateHelper;
+import org.apache.commons.monitoring.reporting.web.template.MapBuilder;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.regex.Matcher;
+
+public class Invoker {
+    private final Object instance;
+    private final Method method;
+    private final String name;
+    private final List<ParameterExtractor<?>> parameters = new CopyOnWriteArrayList<ParameterExtractor<?>>();
+
+    public Invoker(final Object instance, final Method method, final String name) {
+        this.instance = instance;
+        this.method = method;
+        this.name = name;
+    }
+
+    public void invoke(final HttpServletRequest request, final HttpServletResponse response, final Matcher matcher) {
+        final Object[] params = new Object[parameters.size()];
+        for (int i = 0; i < params.length; i++) {
+            params[i] = parameters.get(i).extract(request, response, matcher);
+        }
+        try {
+            final Object result = method.invoke(instance, params);
+            if (Template.class.isInstance(result)) {
+                final Template template = Template.class.cast(result);
+                final TemplateHelper helper = new TemplateHelperExtractor(name).extract(request, response, matcher);
+                if (template.isHtml()) {
+                    helper.renderHtml(template.getTemplate(), template.getUserParams());
+                } else {
+                    helper.renderPlain(template.getTemplate(), template.getUserParams());
+                }
+            } else if (result != null) {
+                response.getWriter().write(result.toString());
+            }
+        } catch (final InvocationTargetException e) {
+            throw new MonitoringException(e.getCause());
+        } catch (final Exception e) {
+            throw new MonitoringException(e);
+        }
+    }
+
+    public void addRequestParameter() {
+        parameters.add(new RequestExtractor());
+    }
+
+    public void addResponseParameter() {
+        parameters.add(new ResponseExtractor());
+    }
+
+    public void addTemplateHelper(final String plugin) {
+        parameters.add(new TemplateHelperExtractor(plugin));
+    }
+
+    public void addSegmentParameter(final Class<?> clazz, final int partIdx) {
+        if (String.class.equals(clazz)) {
+            parameters.add(new StringSegmentExtractor(partIdx));
+        } else if (Long.TYPE.equals(clazz)) {
+            parameters.add(new LongSegmentExtractor(partIdx));
+        } else if (Integer.TYPE.equals(clazz)) {
+            parameters.add(new IntSegmentExtractor(partIdx));
+        } else if (String[].class.equals(clazz)) {
+            parameters.add(new StringArraySegmentExtractor(partIdx));
+        } else {
+            throw new IllegalArgumentException(clazz.getName() + " not handled");
+        }
+    }
+
+    protected static interface ParameterExtractor<T> {
+        T extract(HttpServletRequest request, HttpServletResponse response, Matcher matcher);
+    }
+
+    protected static class TemplateHelperExtractor implements ParameterExtractor<TemplateHelper> {
+        private final String plugin;
+
+        public TemplateHelperExtractor(final String plugin) {
+            this.plugin = plugin;
+        }
+
+        @Override
+        public TemplateHelper extract(final HttpServletRequest request, final HttpServletResponse response, final Matcher matcher) {
+            try {
+                return new TemplateHelper(response.getWriter(), new MapBuilder<String, Object>().set("templateId", plugin).build());
+            } catch (final IOException e) {
+                throw new MonitoringException(e);
+            }
+        }
+    }
+
+    protected static class RequestExtractor implements ParameterExtractor<HttpServletRequest> {
+        @Override
+        public HttpServletRequest extract(final HttpServletRequest request, final HttpServletResponse response, final Matcher matcher) {
+            return request;
+        }
+    }
+
+    protected static class ResponseExtractor implements ParameterExtractor<HttpServletResponse> {
+        @Override
+        public HttpServletResponse extract(final HttpServletRequest request, final HttpServletResponse response, final Matcher matcher) {
+            return response;
+        }
+    }
+
+    protected static class StringSegmentExtractor implements ParameterExtractor<String> {
+        private final int index;
+
+        public StringSegmentExtractor(final int index) {
+            this.index = index;
+        }
+
+        @Override
+        public String extract(final HttpServletRequest request, final HttpServletResponse response, final Matcher matcher) {
+            return matcher.group(index);
+        }
+    }
+
+    protected static class StringArraySegmentExtractor implements ParameterExtractor<String[]> {
+        private final int index;
+
+        public StringArraySegmentExtractor(final int index) {
+            this.index = index;
+        }
+
+        @Override
+        public String[] extract(final HttpServletRequest request, final HttpServletResponse response, final Matcher matcher) {
+            return matcher.group(index).split("/");
+        }
+    }
+
+    protected static class LongSegmentExtractor implements ParameterExtractor<Long> {
+        private final int index;
+
+        public LongSegmentExtractor(final int index) {
+            this.index = index;
+        }
+
+        @Override
+        public Long extract(final HttpServletRequest request, final HttpServletResponse response, final Matcher matcher) {
+            return Long.parseLong(matcher.group(index));
+        }
+    }
+
+    protected static class IntSegmentExtractor implements ParameterExtractor<Integer> {
+        private final int index;
+
+        public IntSegmentExtractor(final int index) {
+            this.index = index;
+        }
+
+        @Override
+        public Integer extract(final HttpServletRequest request, final HttpServletResponse response, final Matcher matcher) {
+            return Integer.parseInt(matcher.group(index));
+        }
+    }
+}

Modified: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/Plugin.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/Plugin.java?rev=1510386&r1=1510385&r2=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/Plugin.java (original)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/Plugin.java Mon Aug  5 08:48:04 2013
@@ -16,8 +16,6 @@
  */
 package org.apache.commons.monitoring.reporting.web.plugin;
 
-import org.apache.commons.monitoring.reporting.web.handler.Handler;
-
 public interface Plugin {
     /**
      * @return plugin name.
@@ -25,12 +23,12 @@ public interface Plugin {
     String name();
 
     /**
-     * @return the handler to call when mappings() are matched.
+     * @return the endpoints to call when mapping() is matched.
      */
-    Class<? extends Handler> handler();
+    Class<?> endpoints();
 
     /**
-     * @return the mapping of the handler if not null. Note: it shouldn't start with '/' but can end with /*.
+     * @return the base mapping of the endpoints if not null.
      */
     String mapping();
 }

Modified: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/PluginRepository.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/PluginRepository.java?rev=1510386&r1=1510385&r2=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/PluginRepository.java (original)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/PluginRepository.java Mon Aug  5 08:48:04 2013
@@ -16,13 +16,15 @@
  */
 package org.apache.commons.monitoring.reporting.web.plugin;
 
-import org.apache.commons.monitoring.MonitoringException;
 import org.apache.commons.monitoring.configuration.Configuration;
-import org.apache.commons.monitoring.reporting.web.handler.Handler;
+import org.apache.commons.monitoring.reporting.web.handler.internal.EndpointInfo;
+import org.apache.commons.monitoring.reporting.web.handler.internal.Invoker;
 
 import java.util.Collection;
+import java.util.Map;
 import java.util.ServiceLoader;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.regex.Pattern;
 
 public final class PluginRepository {
     public static Collection<PluginInfo> PLUGIN_INFO = new CopyOnWriteArrayList<PluginInfo>();
@@ -38,14 +40,9 @@ public final class PluginRepository {
             }
 
             final String mapping = plugin.mapping();
-            final Class<? extends Handler> handler = plugin.handler();
-            if (mapping != null && handler != null) {
-                try {
-                    final Handler handlerInstance = new PluginDecoratorHandler(Configuration.newInstance(handler), name);
-                    PLUGIN_INFO.add(new PluginInfo(mapping, handlerInstance, name));
-                } catch (final Exception e) {
-                    throw new MonitoringException(e);
-                }
+            final Class<?> handler = plugin.endpoints();
+            if (mapping != null) {
+                PLUGIN_INFO.add(new PluginInfo(mapping, name, EndpointInfo.build(handler, plugin.name(), plugin.mapping())));
             }
         }
     }
@@ -56,39 +53,25 @@ public final class PluginRepository {
 
     public static class PluginInfo {
         private final String url;
-        private final Handler handler;
         private final String name;
-        private final String rootUrl;
+        private final EndpointInfo info;
 
-        public PluginInfo(final String url, final Handler handler, final String name) {
+        public PluginInfo(final String url, String name, final EndpointInfo info) {
             this.url = url;
-            this.handler = handler;
             this.name = name;
-            if (!url.endsWith("*")) {
-                rootUrl = url;
-            } else {
-                if (url.endsWith("/")) {
-                    rootUrl = url.substring(0, url.length() - "/*".length());
-                } else {
-                    rootUrl = url.substring(0, url.length() - "*".length());
-                }
-            }
+            this.info = info;
         }
 
         public String getUrl() {
             return url;
         }
 
-        public Handler getHandler() {
-            return handler;
-        }
-
         public String getName() {
             return name;
         }
 
-        public String getRootUrl() {
-            return rootUrl;
+        public Map<Pattern, Invoker> getInvokers() {
+            return info.getInvokers();
         }
     }
 }

Copied: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jmx/JMXEndpoints.java (from r1510370, 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/JMXEndpoints.java?p2=commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jmx/JMXEndpoints.java&p1=commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jmx/JMXHandler.java&r1=1510370&r2=1510386&rev=1510386&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/JMXEndpoints.java Mon Aug  5 08:48:04 2013
@@ -19,10 +19,9 @@ 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.handler.api.Regex;
+import org.apache.commons.monitoring.reporting.web.handler.api.Template;
 import org.apache.commons.monitoring.reporting.web.template.MapBuilder;
-import org.apache.commons.monitoring.reporting.web.template.Templates;
 import org.apache.commons.monitoring.util.ClassLoaders;
 
 import javax.management.MBeanAttributeInfo;
@@ -30,28 +29,22 @@ import javax.management.MBeanInfo;
 import javax.management.MBeanOperationInfo;
 import javax.management.MBeanParameterInfo;
 import javax.management.MBeanServer;
-import javax.management.MalformedObjectNameException;
 import javax.management.ObjectInstance;
 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;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-public class JMXHandler extends HandlerRendererAdapter {
+public class JMXEndpoints {
     private static final MBeanServer SERVER = ManagementFactory.getPlatformMBeanServer();
     private static final boolean METHOD_INVOCATION_ALLOWED = Configuration.is(Configuration.COMMONS_MONITORING_PREFIX + "jmx.method.allowed", true);
 
@@ -69,60 +62,57 @@ public class JMXHandler extends HandlerR
         }
     }
 
+    @Regex("")
+    public Template home() {
+        return new Template("jmx/main.vm", new MapBuilder<String, Object>().set("jmxTree", buildJmxTree()).build());
+    }
 
-    @Override
-    protected Renderer rendererFor(final String path) {
-        if ("/jmx".endsWith(path) || "/jmx/".equals(path)) {
-            return this;
-        }
-
-        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 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], decodedParams);
-            } catch (final MalformedObjectNameException e) {
-                throw new MonitoringException(e);
-            }
+    @Regex("/operation/([^/]*)/([^/]*)/(.*)")
+    public String invokeOperation(final String objectNameBase64, final String method, final String[] parameters) {
+        if (!METHOD_INVOCATION_ALLOWED) {
+            throw new MonitoringException("Method invocation not allowed");
         }
 
         try {
-            return new MBeanRenderer(new ObjectName(new String(Base64.decodeBase64(subPath))));
-        } catch (final MalformedObjectNameException e) {
-            throw new MonitoringException(e);
+            final ObjectName name = new ObjectName(new String(Base64.decodeBase64(objectNameBase64)));
+            final MBeanInfo info = SERVER.getMBeanInfo(name);
+            for (final MBeanOperationInfo op : info.getOperations()) {
+                if (op.getName().equals(method)) {
+                    final MBeanParameterInfo[] signature = op.getSignature();
+                    final String[] sign = new String[signature.length];
+                    for (int i = 0; i < sign.length; i++) {
+                        sign[i] = signature[i].getType();
+                    }
+                    final Object result = SERVER.invoke(name, method, convertParams(signature, parameters), sign);
+                    return "<div>Method was invoked and returned:</div>" + value(result);
+                }
+            }
+        } catch (final Exception e) {
+            return "<div class=\"alert alert-error\">\n" +
+                "\n" + e.getMessage() + "\n" +
+                "</div>";
         }
-    }
 
-    @Override
-    protected String getTemplate() {
-        return "jmx/main.vm";
+        return "<div class=\"alert alert-error\">Operation" + method + " not found.</div>";
     }
 
-    @Override
-    protected Map<String, ?> getVariables() {
-        return new MapBuilder<String, Object>().set("jmxTree", buildJmxTree()).build();
+    @Regex("/([^/]*)")
+    public Template mbean(final String objectNameBase64) {
+        try {
+            final ObjectName name = new ObjectName(new String(Base64.decodeBase64(objectNameBase64)));
+            final MBeanInfo info = SERVER.getMBeanInfo(name);
+            return new Template("templates/jmx/mbean.vm",
+                new MapBuilder<String, Object>()
+                    .set("objectname", name.toString())
+                    .set("objectnameHash", Base64.encodeBase64String(name.toString().getBytes()))
+                    .set("classname", info.getClassName())
+                    .set("description", value(info.getDescription()))
+                    .set("attributes", attributes(name, info))
+                    .set("operations", operations(info))
+                    .build(), false);
+        } catch (final Exception e) {
+            throw new MonitoringException(e);
+        }
     }
 
     private static JMXNode buildJmxTree() {
@@ -136,167 +126,103 @@ public class JMXHandler extends HandlerR
         return root;
     }
 
-    private static class InvokeRenderer implements Renderer {
-        private final ObjectName name;
-        private final String operation;
-        private final String[] params;
-
-        public InvokeRenderer(final ObjectName objectName, final String method, final String[] parameters) {
-            name = objectName;
-            operation = method;
-            params = parameters;
+    private Object[] convertParams(final MBeanParameterInfo[] signature, final String[] params) {
+        if (params == null) {
+            return null;
         }
 
-        @Override
-        public void render(final PrintWriter writer, final Map<String, ?> ignored) {
-            try {
-                final MBeanInfo info = SERVER.getMBeanInfo(name);
-                for (final MBeanOperationInfo op : info.getOperations()) {
-                    if (op.getName().equals(operation)) {
-                        final MBeanParameterInfo[] signature = op.getSignature();
-                        final String[] sign = new String[signature.length];
-                        for (int i = 0; i < sign.length; i++) {
-                            sign[i] = signature[i].getType();
-                        }
-                        final Object result = SERVER.invoke(name, operation, convertParams(signature, params), sign);
-                        writer.write("<div>Method was invoked and returned:</div>" + value(result));
-                        return;
-                    }
-                }
-            } catch (final Exception e) {
-                writer.write("<div class=\"alert alert-error\">\n" +
-                    "\n" + e.getMessage() + "\n" +
-                    "</div>");
-                return;
+        final Object[] convertedParams = new Object[signature.length];
+        for (int i = 0; i < signature.length; i++) {
+            if (i < params.length) {
+                convertedParams[i] = convert(signature[i].getType(), params[i]);
+            } else {
+                convertedParams[i] = null;
             }
-
-            writer.write("<div class=\"alert alert-error\">Operation" + operation + " not found.</div>");
         }
+        return convertedParams;
+    }
 
-        private Object[] convertParams(final MBeanParameterInfo[] signature, final String[] params) {
-            if (params == null) {
-                return null;
-            }
-
-            final Object[] convertedParams = new Object[signature.length];
-            for (int i = 0; i < signature.length; i++) {
-                if (i < params.length) {
-                    convertedParams[i] = convert(signature[i].getType(), params[i]);
+    public static Object convert(final String type, final String value) {
+        try {
+            if (WRAPPERS.containsKey(type)) {
+                if (type.equals(Character.TYPE.getName())) {
+                    return value.charAt(0);
                 } else {
-                    convertedParams[i] = null;
+                    return tryStringConstructor(type, value);
                 }
             }
-            return convertedParams;
-        }
-
-        public static Object convert(final String type, final String value) {
-            try {
-                if (WRAPPERS.containsKey(type)) {
-                    if (type.equals(Character.TYPE.getName())) {
-                        return value.charAt(0);
-                    } else {
-                        return tryStringConstructor(type, value);
-                    }
-                }
-
-                if (type.equals(Character.class.getName())) {
-                    return value.charAt(0);
-                }
-
-                if (Number.class.isAssignableFrom(ClassLoaders.current().loadClass(type))) {
-                    return toNumber(value);
-                }
-
-                if (value == null || value.equals("null")) {
-                    return null;
-                }
 
-                return tryStringConstructor(type, value);
-            } catch (final Exception e) {
-                throw new MonitoringException(e);
+            if (type.equals(Character.class.getName())) {
+                return value.charAt(0);
             }
-        }
 
-        private static Number toNumber(final String value) throws NumberFormatException {
-            // first the user can force the conversion
-            final char lastChar = Character.toLowerCase(value.charAt(value.length() - 1));
-            if (lastChar == 'd') {
-                return Double.valueOf(value.substring(0, value.length() - 1));
-            }
-            if (lastChar == 'l') {
-                return Long.valueOf(value.substring(0, value.length() - 1));
-            }
-            if (lastChar == 'f') {
-                return Float.valueOf(value.substring(0, value.length() - 1));
+            if (Number.class.isAssignableFrom(ClassLoaders.current().loadClass(type))) {
+                return toNumber(value);
             }
 
-            // try all conversions in cascade until it works
-            for (final Class<?> clazz : new Class<?>[] { Integer.class, Long.class, Double.class }) {
-                try {
-                    return Number.class.cast(clazz.getMethod("valueOf").invoke(null, value));
-                } catch (final Exception e) {
-                    // no-op
-                }
+            if (value == null || value.equals("null")) {
+                return null;
             }
 
-            throw new MonitoringException(value + " is not a number");
-        }
-
-        private static Object tryStringConstructor(String type, final String value) throws Exception {
-            return ClassLoaders.current().loadClass(type).getConstructor(String.class).newInstance(value);
+            return tryStringConstructor(type, value);
+        } catch (final Exception e) {
+            throw new MonitoringException(e);
         }
     }
 
-    private static class MBeanRenderer implements Renderer {
-        private final ObjectName name;
-
-        private MBeanRenderer(final ObjectName objectName) {
-            this.name = objectName;
+    private static Number toNumber(final String value) throws NumberFormatException {
+        // first the user can force the conversion
+        final char lastChar = Character.toLowerCase(value.charAt(value.length() - 1));
+        if (lastChar == 'd') {
+            return Double.valueOf(value.substring(0, value.length() - 1));
+        }
+        if (lastChar == 'l') {
+            return Long.valueOf(value.substring(0, value.length() - 1));
+        }
+        if (lastChar == 'f') {
+            return Float.valueOf(value.substring(0, value.length() - 1));
         }
 
-        @Override
-        public void render(final PrintWriter writer, final Map<String, ?> params) {
+        // try all conversions in cascade until it works
+        for (final Class<?> clazz : new Class<?>[] { Integer.class, Long.class, Double.class }) {
             try {
-                final MBeanInfo info = SERVER.getMBeanInfo(name);
-                Templates.render(writer, "templates/jmx/mbean.vm",
-                    new MapBuilder<String, Object>()
-                        .set("objectname", name.toString())
-                        .set("objectnameHash", Base64.encodeBase64String(name.toString().getBytes()))
-                        .set("classname", info.getClassName())
-                        .set("description", value(info.getDescription()))
-                        .set("attributes", attributes(info))
-                        .set("operations", operations(info))
-                        .build());
+                return Number.class.cast(clazz.getMethod("valueOf").invoke(null, value));
             } catch (final Exception e) {
-                throw new MonitoringException(e);
+                // no-op
             }
         }
 
-        private Collection<MBeanOperation> operations(final MBeanInfo info) {
-            final Collection<MBeanOperation> operations = new LinkedList<MBeanOperation>();
-            for (final MBeanOperationInfo operationInfo : info.getOperations()) {
-                final MBeanOperation mBeanOperation = new MBeanOperation(operationInfo.getName(), operationInfo.getReturnType());
-                for (final MBeanParameterInfo param : operationInfo.getSignature()) {
-                    mBeanOperation.getParameters().add(new MBeanParameter(param.getName(), param.getType()));
-                }
-                operations.add(mBeanOperation);
+        throw new MonitoringException(value + " is not a number");
+    }
+
+    private static Object tryStringConstructor(String type, final String value) throws Exception {
+        return ClassLoaders.current().loadClass(type).getConstructor(String.class).newInstance(value);
+    }
+
+    private Collection<MBeanOperation> operations(final MBeanInfo info) {
+        final Collection<MBeanOperation> operations = new LinkedList<MBeanOperation>();
+        for (final MBeanOperationInfo operationInfo : info.getOperations()) {
+            final MBeanOperation mBeanOperation = new MBeanOperation(operationInfo.getName(), operationInfo.getReturnType());
+            for (final MBeanParameterInfo param : operationInfo.getSignature()) {
+                mBeanOperation.getParameters().add(new MBeanParameter(param.getName(), param.getType()));
             }
-            return operations;
+            operations.add(mBeanOperation);
         }
+        return operations;
+    }
 
-        private Collection<MBeanAttribute> attributes(final MBeanInfo info) {
-            final Collection<MBeanAttribute> list = new LinkedList<MBeanAttribute>();
-            for (final MBeanAttributeInfo attribute : info.getAttributes()) {
-                Object value;
-                try {
-                    value = SERVER.getAttribute(name, attribute.getName());
-                } catch (final Exception e) {
-                    value = "<div class=\"alert-error\">" + e.getMessage() + "</div>";
-                }
-                list.add(new MBeanAttribute(attribute.getName(), attribute.getType(), attribute.getDescription(), value(value)));
+    private Collection<MBeanAttribute> attributes(final ObjectName name, final MBeanInfo info) {
+        final Collection<MBeanAttribute> list = new LinkedList<MBeanAttribute>();
+        for (final MBeanAttributeInfo attribute : info.getAttributes()) {
+            Object value;
+            try {
+                value = SERVER.getAttribute(name, attribute.getName());
+            } catch (final Exception e) {
+                value = "<div class=\"alert-error\">" + e.getMessage() + "</div>";
             }
-            return list;
+            list.add(new MBeanAttribute(attribute.getName(), attribute.getType(), attribute.getDescription(), value(value)));
         }
+        return list;
     }
 
     private static String value(final Object value) {

Modified: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jmx/JMXPlugin.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jmx/JMXPlugin.java?rev=1510386&r1=1510385&r2=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jmx/JMXPlugin.java (original)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jmx/JMXPlugin.java Mon Aug  5 08:48:04 2013
@@ -16,7 +16,6 @@
  */
 package org.apache.commons.monitoring.reporting.web.plugin.jmx;
 
-import org.apache.commons.monitoring.reporting.web.handler.Handler;
 import org.apache.commons.monitoring.reporting.web.plugin.Plugin;
 
 public class JMXPlugin implements Plugin {
@@ -26,12 +25,12 @@ public class JMXPlugin implements Plugin
     }
 
     @Override
-    public Class<? extends Handler> handler() {
-        return JMXHandler.class;
+    public Class<?> endpoints() {
+        return JMXEndpoints.class;
     }
 
     @Override
     public String mapping() {
-        return "jmx/*";
+        return "/jmx";
     }
 }

Copied: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jvm/JVMEndpoints.java (from r1510370, commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jvm/JVMHandler.java)
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jvm/JVMEndpoints.java?p2=commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jvm/JVMEndpoints.java&p1=commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jvm/JVMHandler.java&r1=1510370&r2=1510386&rev=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jvm/JVMHandler.java (original)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jvm/JVMEndpoints.java Mon Aug  5 08:48:04 2013
@@ -16,86 +16,55 @@
  */
 package org.apache.commons.monitoring.reporting.web.plugin.jvm;
 
-import org.apache.commons.monitoring.Role;
-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.handler.api.Regex;
+import org.apache.commons.monitoring.reporting.web.handler.api.Template;
 import org.apache.commons.monitoring.reporting.web.plugin.jvm.gauges.CPUGauge;
 import org.apache.commons.monitoring.reporting.web.plugin.jvm.gauges.UsedMemoryGauge;
 import org.apache.commons.monitoring.reporting.web.template.MapBuilder;
 import org.apache.commons.monitoring.repositories.Repository;
 
-import java.io.PrintWriter;
 import java.lang.management.ManagementFactory;
 import java.lang.management.MemoryMXBean;
 import java.lang.management.OperatingSystemMXBean;
 import java.util.Iterator;
 import java.util.Map;
 
-public class JVMHandler extends HandlerRendererAdapter {
-    @Override
-    protected Renderer rendererFor(final String path) {
-        if (isPath("/jvm", path)) {
-            return this;
-        }
-
-        final String[] parts = path.substring("/jvm/".length()).split("/");
-        if (parts.length == 3) {
-            if ("cpu".equals(parts[0])) {
-                return new GaugeJSonRenderer("CPU Usage", CPUGauge.CPU, parts[1], parts[2]);
-            }
-            if ("memory".equals(parts[0])) {
-                return new GaugeJSonRenderer("Used Memory", UsedMemoryGauge.USED_MEMORY, parts[1], parts[2]);
-            }
-        }
-
-        return this;
-    }
-
-    @Override
-    protected String getTemplate() {
-        return "jvm/jvm.vm";
-    }
-
-    @Override
-    protected Map<String,?> getVariables() {
-        final long now = System.currentTimeMillis();
+public class JVMEndpoints {
+    @Regex
+    public Template home() {
         final OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
         final MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
-        return new MapBuilder<String, Object>()
+
+        return new Template("jvm/jvm.vm", new MapBuilder<String, Object>()
             .set("architecture", os.getArch())
             .set("name", os.getName())
             .set("version", os.getVersion())
             .set("numberProcessor", os.getAvailableProcessors())
             .set("maxMemory", memory.getHeapMemoryUsage().getMax())
             .set("initMemory", memory.getHeapMemoryUsage().getInit())
-            .build();
+            .build());
     }
 
-    private static class GaugeJSonRenderer implements Renderer {
-        private final String label;
-        private final Map<Long, Double> data;
-
-        public GaugeJSonRenderer(final String label, final Role role, final String start, final String end) {
-            this.label = label;
-            this.data = Repository.INSTANCE.getGaugeValues(Long.parseLong(start), Long.parseLong(end), role);
-        }
+    @Regex("/cpu/([0-9]*)/([0-9]*)")
+    public String cpu(final long start, final long end) {
+        return "{ \"data\": " + toJson(Repository.INSTANCE.getGaugeValues(start, end, CPUGauge.CPU)) + ", \"label\": \"CPU Usage\", \"color\": \"#317eac\" }";
+    }
 
-        @Override
-        public void render(final PrintWriter writer, final Map<String, ?> params) {
-            writer.write("{ \"data\": " + toJson() + ", \"label\": \"" + label + "\", \"color\": \"#317eac\" }");
-        }
+    @Regex("/memory/([0-9]*)/([0-9]*)")
+    public String memory(final long start, final long end) {
+        return "{ \"data\": " + toJson(Repository.INSTANCE.getGaugeValues(start, end, UsedMemoryGauge.USED_MEMORY)) + ", \"label\": \"Used Memory\", \"color\": \"#317eac\" }";
+    }
 
-        private String toJson() {
-            final StringBuilder builder = new StringBuilder().append("[");
-            final Iterator<Map.Entry<Long,Double>> iterator = data.entrySet().iterator();
-            while (iterator.hasNext()) {
-                final Map.Entry<Long, Double> entry = iterator.next();
-                builder.append("[").append(entry.getKey()).append(", ").append(entry.getValue()).append("]");
-                if (iterator.hasNext()) {
-                    builder.append(", ");
-                }
+    private String toJson(final Map<Long, Double> data) {
+        final StringBuilder builder = new StringBuilder().append("[");
+        final Iterator<Map.Entry<Long,Double>> iterator = data.entrySet().iterator();
+        while (iterator.hasNext()) {
+            final Map.Entry<Long, Double> entry = iterator.next();
+            builder.append("[").append(entry.getKey()).append(", ").append(entry.getValue()).append("]");
+            if (iterator.hasNext()) {
+                builder.append(", ");
             }
-            return builder.append("]").toString();
         }
+        return builder.append("]").toString();
     }
 }

Modified: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jvm/JVMPlugin.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jvm/JVMPlugin.java?rev=1510386&r1=1510385&r2=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jvm/JVMPlugin.java (original)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/jvm/JVMPlugin.java Mon Aug  5 08:48:04 2013
@@ -16,7 +16,6 @@
  */
 package org.apache.commons.monitoring.reporting.web.plugin.jvm;
 
-import org.apache.commons.monitoring.reporting.web.handler.Handler;
 import org.apache.commons.monitoring.reporting.web.plugin.Plugin;
 
 public class JVMPlugin implements Plugin {
@@ -26,12 +25,12 @@ public class JVMPlugin implements Plugin
     }
 
     @Override
-    public Class<? extends Handler> handler() {
-        return JVMHandler.class;
+    public Class<?> endpoints() {
+        return JVMEndpoints.class;
     }
 
     @Override
     public String mapping() {
-        return "jvm*";
+        return "/jvm";
     }
 }

Copied: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ReportEndpoints.java (from r1510370, commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ReportHandler.java)
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ReportEndpoints.java?p2=commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ReportEndpoints.java&p1=commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ReportHandler.java&r1=1510370&r2=1510386&rev=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ReportHandler.java (original)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ReportEndpoints.java Mon Aug  5 08:48:04 2013
@@ -16,43 +16,49 @@
  */
 package org.apache.commons.monitoring.reporting.web.plugin.report;
 
-import org.apache.commons.monitoring.reporting.web.handler.Handler;
-import org.apache.commons.monitoring.reporting.web.handler.Renderer;
+import org.apache.commons.monitoring.MonitoringException;
+import org.apache.commons.monitoring.reporting.web.handler.api.Regex;
+import org.apache.commons.monitoring.reporting.web.handler.api.Template;
 import org.apache.commons.monitoring.reporting.web.plugin.report.format.Format;
+import org.apache.commons.monitoring.repositories.Repository;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 
-public class ReportHandler implements Handler {
-    private final FormatReportHandler html;
-    private final FormatReportHandler csv;
-    private final FormatReportHandler json;
-    private final FormatReportHandler xml;
-    private final ClearHandler clear;
-
-    public ReportHandler() {
-        html = new FormatReportHandler(Format.Defaults.HTML);
-        csv = new FormatReportHandler(Format.Defaults.CSV);
-        json = new FormatReportHandler(Format.Defaults.JSON);
-        xml = new FormatReportHandler(Format.Defaults.XML);
-        clear = new ClearHandler();
-    }
-
-    @Override
-    public Renderer handle(final HttpServletRequest request, final HttpServletResponse response, final String path) {
-        if (path.endsWith(".csv")) {
-            return csv.handle(request, response, path);
-        }
-        if (path.endsWith(".json")) {
-            return json.handle(request, response, path);
-        }
-        if (path.endsWith(".xml")) {
-            return xml.handle(request, response, path);
-        }
-        if (path.endsWith("/clear")) {
-            return clear.handle(request, response, path);
+public class ReportEndpoints {
+    @Regex
+    public Template html(final HttpServletRequest request, final HttpServletResponse response) {
+        return renderFormat(request, response, Format.Defaults.HTML);
+    }
+
+    @Regex(".csv")
+    public Template csv(final HttpServletRequest request, final HttpServletResponse response) {
+        return renderFormat(request, response, Format.Defaults.CSV);
+    }
+
+    @Regex(".json")
+    public Template json(final HttpServletRequest request, final HttpServletResponse response) {
+        return renderFormat(request, response, Format.Defaults.JSON);
+    }
+
+    @Regex(".xml")
+    public Template xml(final HttpServletRequest request, final HttpServletResponse response) {
+        return renderFormat(request, response, Format.Defaults.XML);
+    }
+
+    @Regex("/clear")
+    public void clear(final HttpServletRequest request, final HttpServletResponse response) {
+        Repository.INSTANCE.clear();
+        try {
+            response.sendRedirect(request.getRequestURI().substring(0, request.getRequestURI().length() - "/clear".length()));
+        } catch (final IOException e) {
+            throw new MonitoringException(e);
         }
+    }
 
-        return html.handle(request, response, path);
+    private Template renderFormat(final HttpServletRequest request, final HttpServletResponse response, final Format format) {
+        response.setContentType(format.type());
+        return format.render(request.getParameterMap());
     }
 }

Modified: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ReportPlugin.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ReportPlugin.java?rev=1510386&r1=1510385&r2=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ReportPlugin.java (original)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/ReportPlugin.java Mon Aug  5 08:48:04 2013
@@ -16,7 +16,6 @@
  */
 package org.apache.commons.monitoring.reporting.web.plugin.report;
 
-import org.apache.commons.monitoring.reporting.web.handler.Handler;
 import org.apache.commons.monitoring.reporting.web.plugin.Plugin;
 
 public class ReportPlugin implements Plugin {
@@ -26,12 +25,12 @@ public class ReportPlugin implements Plu
     }
 
     @Override
-    public Class<? extends Handler> handler() {
-        return ReportHandler.class;
+    public Class<?> endpoints() {
+        return ReportEndpoints.class;
     }
 
     @Override
     public String mapping() {
-        return "report*";
+        return "/report";
     }
 }

Modified: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/CSVFormat.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/CSVFormat.java?rev=1510386&r1=1510385&r2=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/CSVFormat.java (original)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/CSVFormat.java Mon Aug  5 08:48:04 2013
@@ -18,8 +18,9 @@ package org.apache.commons.monitoring.re
 
 import org.apache.commons.monitoring.configuration.Configuration;
 import org.apache.commons.monitoring.counters.Unit;
+import org.apache.commons.monitoring.reporting.web.handler.api.Template;
+import org.apache.commons.monitoring.reporting.web.template.MapBuilder;
 
-import java.io.PrintWriter;
 import java.util.Collection;
 import java.util.Map;
 
@@ -27,15 +28,15 @@ public class CSVFormat extends MapFormat
     private static final String SEPARATOR = Configuration.getProperty(Configuration.COMMONS_MONITORING_PREFIX + "csv.separator", ";");
     public static final String HEADER = "Monitor" + SEPARATOR + "Role" + SEPARATOR + toCsv(ATTRIBUTES_ORDERED_LIST);
 
-
     @Override
-    public void render(final PrintWriter writer, final Map<String, ?> params) {
+    public Template render(final Map<String, ?> params) {
         final Unit timeUnit = timeUnit(params);
-
-        writer.write(HEADER);
-        for (final Collection<String> line : snapshot(timeUnit)) {
-            writer.write(toCsv(line));
-        }
+        return new Template("/templates/report/report-csv.vm",
+                        new MapBuilder<String, Object>()
+                        .set("headers", HEADER)
+                        .set("separator", SEPARATOR)
+                        .set("lines", snapshot(timeUnit))
+                        .build(), false);
     }
 
     @Override

Modified: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/Format.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/Format.java?rev=1510386&r1=1510385&r2=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/Format.java (original)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/Format.java Mon Aug  5 08:48:04 2013
@@ -17,10 +17,13 @@
 
 package org.apache.commons.monitoring.reporting.web.plugin.report.format;
 
-import org.apache.commons.monitoring.reporting.web.handler.Renderer;
+import org.apache.commons.monitoring.reporting.web.handler.api.Template;
 
-public interface Format extends Renderer {
+import java.util.Map;
+
+public interface Format {
     String type();
+    Template render(Map<String, ?> params);
 
     public static interface Defaults {
         static final Format JSON = new JSONFormat();

Modified: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/HTMLFormat.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/HTMLFormat.java?rev=1510386&r1=1510385&r2=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/HTMLFormat.java (original)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/HTMLFormat.java Mon Aug  5 08:48:04 2013
@@ -17,17 +17,16 @@
 package org.apache.commons.monitoring.reporting.web.plugin.report.format;
 
 import org.apache.commons.monitoring.counters.Unit;
+import org.apache.commons.monitoring.reporting.web.handler.api.Template;
 import org.apache.commons.monitoring.reporting.web.template.MapBuilder;
-import org.apache.commons.monitoring.reporting.web.template.Templates;
 
-import java.io.PrintWriter;
 import java.util.Map;
 
 public class HTMLFormat extends MapFormat implements Format {
     @Override
-    public void render(final PrintWriter writer, final Map<String, ?> params) {
+    public Template render(final Map<String, ?> params) {
         final Unit timeUnit = timeUnit(params);
-        Templates.htmlRender(writer, "report/report.vm",
+        return new Template("report/report.vm",
             new MapBuilder<String, Object>()
                 .set(Map.class.cast(params))
                 .set("headers", ATTRIBUTES_ORDERED_LIST)

Modified: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/JSONFormat.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/JSONFormat.java?rev=1510386&r1=1510385&r2=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/JSONFormat.java (original)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/JSONFormat.java Mon Aug  5 08:48:04 2013
@@ -17,36 +17,20 @@
 
 package org.apache.commons.monitoring.reporting.web.plugin.report.format;
 
-import org.apache.commons.monitoring.counters.Counter;
+import org.apache.commons.monitoring.reporting.web.handler.api.Template;
+import org.apache.commons.monitoring.reporting.web.template.MapBuilder;
 import org.apache.commons.monitoring.repositories.Repository;
 
-import java.io.PrintWriter;
-import java.util.Iterator;
 import java.util.Map;
 
 public class JSONFormat implements Format {
-    private static final MetricData[] METRIC_DATA = MetricData.values();
-
     @Override
-    public void render(final PrintWriter writer, final Map<String, ?> params) {
-        writer.write("{\"counters\":[");
-        final Iterator<Counter> counters = Repository.INSTANCE.iterator();
-        while (counters.hasNext()) {
-            final Counter counter = counters.next();
-            writer.write("{\"name\":\"" + counter.getKey().getName() + "\",\"role\":\"" + counter.getKey().getRole().getName() + "\",");
-            writer.write("\"unit\":\"" + counter.getKey().getRole().getUnit().getName() + "\",");
-            for (int i = 0; i < METRIC_DATA.length; i++) {
-                writer.write("\"" + METRIC_DATA[i].name() + "\":\"" + METRIC_DATA[i].value(counter) + "\"");
-                if (i < METRIC_DATA.length - 1) {
-                    writer.write(",");
-                }
-            }
-            writer.write("}");
-            if (counters.hasNext()) {
-                writer.write(",");
-            }
-        }
-        writer.write("]}");
+    public Template render(final Map<String, ?> params) {
+        return new Template("/templates/report/report-json.vm",
+            new MapBuilder<String, Object>()
+                .set("MetricData", MetricData.class)
+                .set("counters", Repository.INSTANCE.iterator())
+                .build(), false);
     }
 
     @Override

Modified: commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/XMLFormat.java
URL: http://svn.apache.org/viewvc/commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/XMLFormat.java?rev=1510386&r1=1510385&r2=1510386&view=diff
==============================================================================
--- commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/XMLFormat.java (original)
+++ commons/sandbox/monitoring/trunk/reporting/src/main/java/org/apache/commons/monitoring/reporting/web/plugin/report/format/XMLFormat.java Mon Aug  5 08:48:04 2013
@@ -17,24 +17,20 @@
 
 package org.apache.commons.monitoring.reporting.web.plugin.report.format;
 
-import org.apache.commons.monitoring.counters.Counter;
+import org.apache.commons.monitoring.reporting.web.handler.api.Template;
+import org.apache.commons.monitoring.reporting.web.template.MapBuilder;
 import org.apache.commons.monitoring.repositories.Repository;
 
-import java.io.PrintWriter;
 import java.util.Map;
 
 public class XMLFormat implements Format {
     @Override
-    public void render(final PrintWriter writer, final Map<String, ?> params) {
-        writer.write("<repository>");
-        for (final Counter counter : Repository.INSTANCE) {
-            writer.write("<counter name=\"" + counter.getKey().getName() + "\" role=\"" + counter.getKey().getRole().getName() + "\" unit=\"" + counter.getKey().getRole().getUnit().getName() + "\"");
-            for (final MetricData md : MetricData.values()) {
-                writer.write(" " + md.name() + "=\"" + md.value(counter) + "\"");
-            }
-            writer.write(" />");
-        }
-        writer.write("</repository>");
+    public Template render(final Map<String, ?> params) {
+        return new Template("/templates/report/report-xml.vm",
+            new MapBuilder<String, Object>()
+                .set("MetricData", MetricData.class)
+                .set("counters", Repository.INSTANCE.iterator())
+                .build(), false);
     }
 
     @Override