SLING-4080 - API to capture/measure application-level metrics
Moving to trunk from sandbox

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1723456 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..5d13870
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.sling</groupId>
+    <artifactId>sling</artifactId>
+    <version>26</version>
+  </parent>
+
+  <artifactId>org.apache.sling.metrics</artifactId>
+  <packaging>bundle</packaging>
+  <version>0.0.1-SNAPSHOT</version>
+
+  <name>Apache Sling Metrics</name>
+  <description>
+    Integrates Metric library http://metrics.dropwizard.io/ with Sling
+
+    Refer to http://sling.apache.org/documentation/bundles/log-tracers.html
+  </description>
+
+  <scm>
+    <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/metrics</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/metrics</developerConnection>
+    <url>http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/metrics</url>
+  </scm>
+
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-scr-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.dropwizard.metrics</groupId>
+      <artifactId>metrics-core</artifactId>
+      <version>3.1.0</version>
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>servlet-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.commons.osgi</artifactId>
+      <version>2.2.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <version>4.3.1</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <version>4.3.1</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.inventory</artifactId>
+      <version>1.0.2</version>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+      <version>2.2</version>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.testing.osgi-mock</artifactId>
+      <version>1.3.0</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <version>1.10.19</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/metrics/Counter.java b/src/main/java/org/apache/sling/metrics/Counter.java
new file mode 100644
index 0000000..b828aa5
--- /dev/null
+++ b/src/main/java/org/apache/sling/metrics/Counter.java
@@ -0,0 +1,49 @@
+/*
+ * 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.sling.metrics;
+
+import aQute.bnd.annotation.ProviderType;
+
+@ProviderType
+public interface Counter extends Counting, Metric{
+    /**
+     * Increment the counter by one.
+     */
+    void inc();
+
+    /**
+     * Decrement the counter by one.
+     */
+    void dec();
+
+    /**
+     * Increment the counter by {@code n}.
+     *
+     * @param n the amount by which the counter will be increased
+     */
+    void inc(long n);
+
+    /**
+     * Decrement the counter by {@code n}.
+     *
+     * @param n the amount by which the counter will be decreased
+     */
+    void dec(long n);
+}
diff --git a/src/main/java/org/apache/sling/metrics/Counting.java b/src/main/java/org/apache/sling/metrics/Counting.java
new file mode 100644
index 0000000..309decd
--- /dev/null
+++ b/src/main/java/org/apache/sling/metrics/Counting.java
@@ -0,0 +1,32 @@
+/*
+ * 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.sling.metrics;
+
+import aQute.bnd.annotation.ProviderType;
+
+@ProviderType
+public interface Counting {
+    /**
+     * Returns the current count.
+     *
+     * @return the current count
+     */
+    long getCount();
+}
diff --git a/src/main/java/org/apache/sling/metrics/Histogram.java b/src/main/java/org/apache/sling/metrics/Histogram.java
new file mode 100644
index 0000000..37938f5
--- /dev/null
+++ b/src/main/java/org/apache/sling/metrics/Histogram.java
@@ -0,0 +1,32 @@
+/*
+ * 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.sling.metrics;
+
+import aQute.bnd.annotation.ProviderType;
+
+@ProviderType
+public interface Histogram extends Counting, Metric {
+    /**
+     * Adds a recorded value.
+     *
+     * @param value the length of the value
+     */
+    void update(long value);
+}
diff --git a/src/main/java/org/apache/sling/metrics/Meter.java b/src/main/java/org/apache/sling/metrics/Meter.java
new file mode 100644
index 0000000..a4914d7
--- /dev/null
+++ b/src/main/java/org/apache/sling/metrics/Meter.java
@@ -0,0 +1,37 @@
+/*
+ * 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.sling.metrics;
+
+import aQute.bnd.annotation.ProviderType;
+
+@ProviderType
+public interface Meter extends Counting, Metric{
+    /**
+     * Mark the occurrence of an event.
+     */
+    void mark();
+
+    /**
+     * Mark the occurrence of a given number of events.
+     *
+     * @param n the number of events
+     */
+    void mark(long n);
+}
diff --git a/src/main/java/org/apache/sling/metrics/Metric.java b/src/main/java/org/apache/sling/metrics/Metric.java
new file mode 100644
index 0000000..4952017
--- /dev/null
+++ b/src/main/java/org/apache/sling/metrics/Metric.java
@@ -0,0 +1,35 @@
+/*
+ * 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.sling.metrics;
+
+import aQute.bnd.annotation.ProviderType;
+
+@ProviderType
+public interface Metric {
+    /**
+     * Adapts the Metric to the specified type.
+     *
+     * @param <A> The type to which this metric is to be adapted.
+     * @param type Class object for the type to which this metric is to be adapted.
+     * @return The object, of the specified type, to which this metric has been adapted
+     * or null if this metric cannot be adapted to the specified type.
+     */
+    <A> A adaptTo(Class<A> type);
+}
diff --git a/src/main/java/org/apache/sling/metrics/MetricsService.java b/src/main/java/org/apache/sling/metrics/MetricsService.java
new file mode 100644
index 0000000..8c51d4b
--- /dev/null
+++ b/src/main/java/org/apache/sling/metrics/MetricsService.java
@@ -0,0 +1,79 @@
+/*
+ * 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.sling.metrics;
+
+import aQute.bnd.annotation.ProviderType;
+
+@ProviderType
+public interface MetricsService {
+    MetricsService NOOP = new MetricsService() {
+        @Override
+        public Timer timer(String name) {
+            return NoopMetric.INSTANCE;
+        }
+
+        @Override
+        public Histogram histogram(String name) {
+            return NoopMetric.INSTANCE;
+        }
+
+        @Override
+        public Counter counter(String name) {
+            return NoopMetric.INSTANCE;
+        }
+
+        @Override
+        public Meter meter(String name) {
+            return NoopMetric.INSTANCE;
+        }
+    };
+
+    /**
+     * Creates a new {@link Timer} and registers it under the given name.
+     *
+     * @param name the name of the metric
+     * @return a new {@link Timer}
+     */
+    Timer timer(String name);
+
+    /**
+     * Creates a new {@link Histogram} and registers it under the given name.
+     *
+     * @param name the name of the metric
+     * @return a new {@link Histogram}
+     */
+    Histogram histogram(String name);
+
+    /**
+     * Creates a new {@link Counter} and registers it under the given name.
+     *
+     * @param name the name of the metric
+     * @return a new {@link Counter}
+     */
+    Counter counter(String name);
+
+    /**
+     * Creates a new {@link Meter} and registers it under the given name.
+     *
+     * @param name the name of the metric
+     * @return a new {@link Meter}
+     */
+    Meter meter(String name);
+}
diff --git a/src/main/java/org/apache/sling/metrics/NoopMetric.java b/src/main/java/org/apache/sling/metrics/NoopMetric.java
new file mode 100644
index 0000000..210fb3c
--- /dev/null
+++ b/src/main/java/org/apache/sling/metrics/NoopMetric.java
@@ -0,0 +1,94 @@
+/*
+ * 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.sling.metrics;
+
+import java.util.concurrent.TimeUnit;
+
+final class NoopMetric implements Counter, Histogram, Timer, Meter{
+    public static final NoopMetric INSTANCE = new NoopMetric();
+    @Override
+    public long getCount() {
+        return 0;
+    }
+
+    @Override
+    public void inc() {
+
+    }
+
+    @Override
+    public void dec() {
+
+    }
+
+    @Override
+    public void inc(long n) {
+
+    }
+
+    @Override
+    public void dec(long n) {
+
+    }
+
+    @Override
+    public void mark() {
+
+    }
+
+    @Override
+    public void mark(long n) {
+
+    }
+
+    @Override
+    public void update(long duration, TimeUnit unit) {
+
+    }
+
+    @Override
+    public Context time() {
+        return NoopContext.INSTANCE;
+    }
+
+    @Override
+    public void update(long value) {
+
+    }
+
+    @Override
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+        return null;
+    }
+
+    private static final class NoopContext implements Context {
+        public static final NoopContext INSTANCE = new NoopContext();
+
+        @Override
+        public long stop() {
+            return 0;
+        }
+
+        @Override
+        public void close() {
+
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/metrics/Timer.java b/src/main/java/org/apache/sling/metrics/Timer.java
new file mode 100644
index 0000000..602a0cb
--- /dev/null
+++ b/src/main/java/org/apache/sling/metrics/Timer.java
@@ -0,0 +1,58 @@
+/*
+ * 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.sling.metrics;
+
+import java.io.Closeable;
+import java.util.concurrent.TimeUnit;
+
+import aQute.bnd.annotation.ProviderType;
+
+@ProviderType
+public interface Timer extends Counting, Metric{
+    /**
+     * A timing context.
+     *
+     * @see Timer#time()
+     */
+    interface Context extends Closeable {
+        /**
+         * Updates the timer with the difference between current and start time. Call to this method will
+         * not reset the start time. Multiple calls result in multiple updates.
+         * @return the elapsed time in nanoseconds
+         */
+        long stop();
+    }
+
+    /**
+     * Adds a recorded duration.
+     *
+     * @param duration the length of the duration
+     * @param unit     the scale unit of {@code duration}
+     */
+    void update(long duration, TimeUnit unit);
+
+    /**
+     * Returns a new {@link Context}.
+     *
+     * @return a new {@link Context}
+     * @see Context
+     */
+    Context time();
+}
diff --git a/src/main/java/org/apache/sling/metrics/internal/CounterImpl.java b/src/main/java/org/apache/sling/metrics/internal/CounterImpl.java
new file mode 100644
index 0000000..0137b71
--- /dev/null
+++ b/src/main/java/org/apache/sling/metrics/internal/CounterImpl.java
@@ -0,0 +1,65 @@
+/*
+ * 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.sling.metrics.internal;
+
+
+import org.apache.sling.metrics.Counter;
+
+final class CounterImpl implements Counter {
+    private final com.codahale.metrics.Counter counter;
+
+    CounterImpl(com.codahale.metrics.Counter counter) {
+        this.counter = counter;
+    }
+
+    @Override
+    public void inc() {
+        counter.inc();
+    }
+
+    @Override
+    public void dec() {
+        counter.dec();
+    }
+
+    @Override
+    public void inc(long n) {
+        counter.inc(n);
+    }
+
+    @Override
+    public void dec(long n) {
+        counter.dec(n);
+    }
+
+    @Override
+    public long getCount() {
+        return counter.getCount();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <A> A adaptTo(Class<A> type) {
+        if (type == com.codahale.metrics.Counter.class){
+            return (A) counter;
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/org/apache/sling/metrics/internal/HistogramImpl.java b/src/main/java/org/apache/sling/metrics/internal/HistogramImpl.java
new file mode 100644
index 0000000..15117e9
--- /dev/null
+++ b/src/main/java/org/apache/sling/metrics/internal/HistogramImpl.java
@@ -0,0 +1,50 @@
+/*
+ * 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.sling.metrics.internal;
+
+
+import org.apache.sling.metrics.Histogram;
+
+final class HistogramImpl implements Histogram {
+    private final com.codahale.metrics.Histogram histogram;
+
+    HistogramImpl(com.codahale.metrics.Histogram histogram) {
+        this.histogram = histogram;
+    }
+
+    @Override
+    public void update(long value) {
+        histogram.update(value);
+    }
+
+    @Override
+    public long getCount() {
+        return histogram.getCount();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <A> A adaptTo(Class<A> type) {
+        if (type == com.codahale.metrics.Histogram.class){
+            return (A) histogram;
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/org/apache/sling/metrics/internal/MeterImpl.java b/src/main/java/org/apache/sling/metrics/internal/MeterImpl.java
new file mode 100644
index 0000000..719d356
--- /dev/null
+++ b/src/main/java/org/apache/sling/metrics/internal/MeterImpl.java
@@ -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.sling.metrics.internal;
+
+import org.apache.sling.metrics.Meter;
+
+final class MeterImpl implements Meter {
+    private final com.codahale.metrics.Meter meter;
+
+    MeterImpl(com.codahale.metrics.Meter meter) {
+        this.meter = meter;
+    }
+
+    @Override
+    public void mark() {
+        meter.mark();
+    }
+
+    @Override
+    public void mark(long n) {
+        meter.mark(n);
+    }
+
+    @Override
+    public long getCount() {
+        return meter.getCount();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <A> A adaptTo(Class<A> type) {
+        if (type == com.codahale.metrics.Meter.class){
+            return (A)meter;
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/org/apache/sling/metrics/internal/MetricWebConsolePlugin.java b/src/main/java/org/apache/sling/metrics/internal/MetricWebConsolePlugin.java
new file mode 100644
index 0000000..180acca
--- /dev/null
+++ b/src/main/java/org/apache/sling/metrics/internal/MetricWebConsolePlugin.java
@@ -0,0 +1,475 @@
+/*
+ * 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.sling.metrics.internal;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.Servlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.codahale.metrics.ConsoleReporter;
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Snapshot;
+import com.codahale.metrics.Timer;
+import org.apache.commons.io.output.WriterOutputStream;
+import org.apache.felix.inventory.Format;
+import org.apache.felix.inventory.InventoryPrinter;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component
+@Service(value = {InventoryPrinter.class, Servlet.class})
+@Properties({
+        @Property(name = "felix.webconsole.label", value = "slingmetrics"),
+        @Property(name = "felix.webconsole.title", value = "Metrics"),
+        @Property(name = "felix.webconsole.category", value = "Sling"),
+        @Property(name = InventoryPrinter.FORMAT, value = {"TEXT" }),
+        @Property(name = InventoryPrinter.NAME, value = "slingmetrics"),
+        @Property(name = InventoryPrinter.TITLE, value = "Sling Metrics"),
+        @Property(name = InventoryPrinter.WEBCONSOLE, boolValue = true)
+})
+public class MetricWebConsolePlugin extends HttpServlet implements
+        InventoryPrinter, ServiceTrackerCustomizer<MetricRegistry, MetricRegistry>{
+    /**
+     * Service property name which stores the MetricRegistry name as a given OSGi
+     * ServiceRegistry might have multiple instances of MetricRegistry
+     */
+    public static final String METRIC_REGISTRY_NAME = "name";
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private BundleContext context;
+    private ServiceTracker<MetricRegistry, MetricRegistry> tracker;
+    private ConcurrentMap<ServiceReference, MetricRegistry> registries
+            = new ConcurrentHashMap<ServiceReference, MetricRegistry>();
+
+    private TimeUnit rateUnit = TimeUnit.SECONDS;
+    private TimeUnit durationUnit = TimeUnit.MILLISECONDS;
+    private Map<String, TimeUnit> specificDurationUnits = Collections.emptyMap();
+    private Map<String, TimeUnit> specificRateUnits = Collections.emptyMap();
+    private MetricTimeUnits timeUnit;
+
+    @Activate
+    private void activate(BundleContext context){
+        this.context = context;
+        this.timeUnit = new MetricTimeUnits(rateUnit, durationUnit, specificRateUnits, specificDurationUnits);
+        tracker = new ServiceTracker<MetricRegistry, MetricRegistry>(context, MetricRegistry.class, this);
+        tracker.open();
+    }
+
+    @Deactivate
+    private void deactivate(BundleContext context){
+        tracker.close();
+    }
+
+    //~--------------------------------------------< InventoryPrinter >
+
+    @Override
+    public void print(PrintWriter printWriter, Format format, boolean isZip) {
+        if (format == Format.TEXT) {
+            MetricRegistry registry = getConsolidatedRegistry();
+            ConsoleReporter reporter = ConsoleReporter.forRegistry(registry)
+                    .outputTo(new PrintStream(new WriterOutputStream(printWriter)))
+                    .build();
+            reporter.report();
+            reporter.close();
+        }
+    }
+
+
+    //~---------------------------------------------< ServiceTracker >
+
+    @Override
+    public MetricRegistry addingService(ServiceReference<MetricRegistry> serviceReference) {
+        MetricRegistry registry = context.getService(serviceReference);
+        registries.put(serviceReference, registry);
+        return registry;
+    }
+
+    @Override
+    public void modifiedService(ServiceReference<MetricRegistry> serviceReference, MetricRegistry registry) {
+        registries.put(serviceReference, registry);
+    }
+
+    @Override
+    public void removedService(ServiceReference<MetricRegistry> serviceReference, MetricRegistry registry) {
+        registries.remove(serviceReference);
+    }
+
+    //~----------------------------------------------< Servlet >
+
+    @Override
+    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
+        final PrintWriter pw = resp.getWriter();
+        MetricRegistry registry = getConsolidatedRegistry();
+
+        appendMetricStatus(pw, registry);
+        addCounterDetails(pw, registry.getCounters());
+        addGaugeDetails(pw, registry.getGauges());
+        addMeterDetails(pw, registry.getMeters());
+        addTimerDetails(pw, registry.getTimers());
+        addHistogramDetails(pw, registry.getHistograms());
+    }
+
+    private static void appendMetricStatus(PrintWriter pw, MetricRegistry registry) {
+        pw.printf(
+                "<p class='statline'>Metrics: %d gauges, %d timers, %d meters, %d counters, %d histograms</p>%n",
+                registry.getGauges().size(),
+                registry.getTimers().size(),
+                registry.getMeters().size(),
+                registry.getCounters().size(),
+                registry.getHistograms().size());
+    }
+
+    private void addMeterDetails(PrintWriter pw, SortedMap<String, Meter> meters) {
+        if (meters.isEmpty()) {
+            return;
+        }
+        pw.println("<br>");
+        pw.println("<div class='table'>");
+        pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>Meters</div>");
+        pw.println("<table class='nicetable'>");
+        pw.println("<thead>");
+        pw.println("<tr>");
+        pw.println("<th class='header'>Name</th>");
+        pw.println("<th class='header'>Count</th>");
+        pw.println("<th class='header'>Mean Rate</th>");
+        pw.println("<th class='header'>OneMinuteRate</th>");
+        pw.println("<th class='header'>FiveMinuteRate</th>");
+        pw.println("<th class='header'>FifteenMinuteRate</ th>");
+        pw.println("<th>RateUnit</th>");
+        pw.println("</tr>");
+        pw.println("</thead>");
+        pw.println("<tbody>");
+
+        String rowClass = "odd";
+        for (Map.Entry<String, Meter> e : meters.entrySet()) {
+            Meter m = e.getValue();
+            String name = e.getKey();
+
+            double rateFactor = timeUnit.rateFor(name).toSeconds(1);
+            String rateUnit = "events/" + calculateRateUnit(timeUnit.rateFor(name));
+            pw.printf("<tr class='%s ui-state-default'>%n", rowClass);
+
+            pw.printf("<td>%s</td>", name);
+            pw.printf("<td>%d</td>", m.getCount());
+            pw.printf("<td>%f</td>", m.getMeanRate() * rateFactor);
+            pw.printf("<td>%f</td>", m.getOneMinuteRate() * rateFactor);
+            pw.printf("<td>%f</td>", m.getFiveMinuteRate() * rateFactor);
+            pw.printf("<td>%f</td>", m.getFifteenMinuteRate() * rateFactor);
+            pw.printf("<td>%s</td>", rateUnit);
+
+            pw.println("</tr>");
+            rowClass = "odd".equals(rowClass) ? "even" : "odd";
+        }
+
+        pw.println("</tbody>");
+        pw.println("</table>");
+        pw.println("</div>");
+    }
+
+    private void addTimerDetails(PrintWriter pw, SortedMap<String, Timer> timers) {
+        if (timers.isEmpty()) {
+            return;
+        }
+
+        pw.println("<br>");
+        pw.println("<div class='table'>");
+        pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>Timers</div>");
+        pw.println("<table class='nicetable'>");
+        pw.println("<thead>");
+        pw.println("<tr>");
+        pw.println("<th class='header'>Name</th>");
+        pw.println("<th class='header'>Count</th>");
+        pw.println("<th class='header'>Mean Rate</th>");
+        pw.println("<th class='header'>1 min rate</th>");
+        pw.println("<th class='header'>5 mins rate</th>");
+        pw.println("<th class='header'>15 mins rate</th>");
+        pw.println("<th class='header'>50%</th>");
+        pw.println("<th class='header'>Min</th>");
+        pw.println("<th class='header'>Max</th>");
+        pw.println("<th class='header'>Mean</th>");
+        pw.println("<th class='header'>StdDev</th>");
+        pw.println("<th class='header'>75%</th>");
+        pw.println("<th class='header'>95%</th>");
+        pw.println("<th class='header'>98%</th>");
+        pw.println("<th class='header'>99%</th>");
+        pw.println("<th class='header'>999%</th>");
+        pw.println("<th>Rate Unit</th>");
+        pw.println("<th>Duration Unit</th>");
+        pw.println("</tr>");
+        pw.println("</thead>");
+        pw.println("<tbody>");
+
+        String rowClass = "odd";
+        for (Map.Entry<String, Timer> e : timers.entrySet()) {
+            Timer t = e.getValue();
+            Snapshot s = t.getSnapshot();
+            String name = e.getKey();
+
+            double rateFactor = timeUnit.rateFor(name).toSeconds(1);
+            String rateUnit = "events/" + calculateRateUnit(timeUnit.rateFor(name));
+
+            double durationFactor = 1.0 / timeUnit.durationFor(name).toNanos(1);
+            String durationUnit = timeUnit.durationFor(name).toString().toLowerCase(Locale.US);
+
+            pw.printf("<tr class='%s ui-state-default'>%n", rowClass);
+
+            pw.printf("<td>%s</td>", name);
+            pw.printf("<td>%d</td>", t.getCount());
+            pw.printf("<td>%f</td>", t.getMeanRate() * rateFactor);
+            pw.printf("<td>%f</td>", t.getOneMinuteRate() * rateFactor);
+            pw.printf("<td>%f</td>", t.getFiveMinuteRate() * rateFactor);
+            pw.printf("<td>%f</td>", t.getFifteenMinuteRate() * rateFactor);
+
+            pw.printf("<td>%f</td>", s.getMedian() * durationFactor);
+            pw.printf("<td>%f</td>", s.getMin() * durationFactor);
+            pw.printf("<td>%f</td>", s.getMax() * durationFactor);
+            pw.printf("<td>%f</td>", s.getMean() * durationFactor);
+            pw.printf("<td>%f</td>", s.getStdDev() * durationFactor);
+
+            pw.printf("<td>%f</td>", s.get75thPercentile() * durationFactor);
+            pw.printf("<td>%f</td>", s.get95thPercentile() * durationFactor);
+            pw.printf("<td>%f</td>", s.get98thPercentile() * durationFactor);
+            pw.printf("<td>%f</td>", s.get99thPercentile() * durationFactor);
+            pw.printf("<td>%f</td>", s.get999thPercentile() * durationFactor);
+
+            pw.printf("<td>%s</td>", rateUnit);
+            pw.printf("<td>%s</td>", durationUnit);
+
+            pw.println("</tr>");
+            rowClass = "odd".equals(rowClass) ? "even" : "odd";
+        }
+
+        pw.println("</tbody>");
+        pw.println("</table>");
+        pw.println("</div>");
+    }
+
+    private void addHistogramDetails(PrintWriter pw, SortedMap<String, Histogram> histograms) {
+        if (histograms.isEmpty()) {
+            return;
+        }
+
+        pw.println("<br>");
+        pw.println("<div class='table'>");
+        pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>Histograms</div>");
+        pw.println("<table class='nicetable'>");
+        pw.println("<thead>");
+        pw.println("<tr>");
+        pw.println("<th class='header'>Name</th>");
+        pw.println("<th class='header'>Count</th>");
+        pw.println("<th class='header'>50%</th>");
+        pw.println("<th class='header'>Min</th>");
+        pw.println("<th class='header'>Max</th>");
+        pw.println("<th class='header'>Mean</th>");
+        pw.println("<th class='header'>StdDev</th>");
+        pw.println("<th class='header'>75%</th>");
+        pw.println("<th class='header'>95%</th>");
+        pw.println("<th class='header'>98%</th>");
+        pw.println("<th class='header'>99%</th>");
+        pw.println("<th class='header'>999%</th>");
+        pw.println("<th>Duration Unit</th>");
+        pw.println("</tr>");
+        pw.println("</thead>");
+        pw.println("<tbody>");
+
+        String rowClass = "odd";
+        for (Map.Entry<String, Histogram> e : histograms.entrySet()) {
+            Histogram h = e.getValue();
+            Snapshot s = h.getSnapshot();
+            String name = e.getKey();
+
+            double durationFactor = 1.0 / timeUnit.durationFor(name).toNanos(1);
+            String durationUnit = timeUnit.durationFor(name).toString().toLowerCase(Locale.US);
+            pw.printf("<tr class='%s ui-state-default'>%n", rowClass);
+
+            pw.printf("<td>%s</td>", name);
+            pw.printf("<td>%d</td>", h.getCount());
+            pw.printf("<td>%f</td>", s.getMedian() * durationFactor);
+            pw.printf("<td>%f</td>", s.getMin() * durationFactor);
+            pw.printf("<td>%f</td>", s.getMax() * durationFactor);
+            pw.printf("<td>%f</td>", s.getMean() * durationFactor);
+            pw.printf("<td>%f</td>", s.getStdDev() * durationFactor);
+
+            pw.printf("<td>%f</td>", s.get75thPercentile() * durationFactor);
+            pw.printf("<td>%f</td>", s.get95thPercentile() * durationFactor);
+            pw.printf("<td>%f</td>", s.get98thPercentile() * durationFactor);
+            pw.printf("<td>%f</td>", s.get99thPercentile() * durationFactor);
+            pw.printf("<td>%f</td>", s.get999thPercentile() * durationFactor);
+
+            pw.printf("<td>%s</td>", durationUnit);
+
+            pw.println("</tr>");
+            rowClass = "odd".equals(rowClass) ? "even" : "odd";
+        }
+
+        pw.println("</tbody>");
+        pw.println("</table>");
+        pw.println("</div>");
+    }
+
+    private void addCounterDetails(PrintWriter pw, SortedMap<String, Counter> counters) {
+        if (counters.isEmpty()) {
+            return;
+        }
+        pw.println("<br>");
+        pw.println("<div class='table'>");
+        pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>Counters</div>");
+        pw.println("<table class='nicetable'>");
+        pw.println("<thead>");
+        pw.println("<tr>");
+        pw.println("<th class='header'>Name</th>");
+        pw.println("<th class='header'>Count</th>");
+        pw.println("</tr>");
+        pw.println("</thead>");
+        pw.println("<tbody>");
+
+        String rowClass = "odd";
+        for (Map.Entry<String, Counter> e : counters.entrySet()) {
+            Counter c = e.getValue();
+            String name = e.getKey();
+
+            pw.printf("<tr class='%s ui-state-default'>%n", rowClass);
+
+            pw.printf("<td>%s</td>", name);
+            pw.printf("<td>%d</td>", c.getCount());
+
+            pw.println("</tr>");
+            rowClass = "odd".equals(rowClass) ? "even" : "odd";
+        }
+
+        pw.println("</tbody>");
+        pw.println("</table>");
+        pw.println("</div>");
+    }
+
+    private void addGaugeDetails(PrintWriter pw, SortedMap<String, Gauge> gauges) {
+        if (gauges.isEmpty()) {
+            return;
+        }
+
+        pw.println("<br>");
+        pw.println("<div class='table'>");
+        pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>Guages</div>");
+        pw.println("<table class='nicetable'>");
+        pw.println("<thead>");
+        pw.println("<tr>");
+        pw.println("<th class='header'>Name</th>");
+        pw.println("<th class='header'>Value</th>");
+        pw.println("</tr>");
+        pw.println("</thead>");
+        pw.println("<tbody>");
+
+        String rowClass = "odd";
+        for (Map.Entry<String, Gauge> e : gauges.entrySet()) {
+            Gauge g = e.getValue();
+            String name = e.getKey();
+
+            pw.printf("<tr class='%s ui-state-default'>%n", rowClass);
+
+            pw.printf("<td>%s</td>", name);
+            pw.printf("<td>%s</td>", g.getValue());
+
+            pw.println("</tr>");
+            rowClass = "odd".equals(rowClass) ? "even" : "odd";
+        }
+
+        pw.println("</tbody>");
+        pw.println("</table>");
+        pw.println("</div>");
+    }
+
+
+    //~----------------------------------------------< internal >
+
+    private MetricRegistry getConsolidatedRegistry() {
+        MetricRegistry registry = new MetricRegistry();
+        for (Map.Entry<ServiceReference, MetricRegistry> registryEntry : registries.entrySet()){
+            String metricRegistryName = (String) registryEntry.getKey().getProperty(METRIC_REGISTRY_NAME);
+            for (Map.Entry<String, Metric> metricEntry : registryEntry.getValue().getMetrics().entrySet()){
+                String metricName = metricEntry.getKey();
+                try{
+                    if (metricRegistryName != null){
+                        metricName = metricRegistryName + ":" + metricName;
+                    }
+                    registry.register(metricName, metricEntry.getValue());
+                }catch (IllegalArgumentException ex){
+                    log.warn("Duplicate Metric name found {}", metricName, ex);
+                }
+            }
+        }
+        return registry;
+    }
+
+    private static String calculateRateUnit(TimeUnit unit) {
+        final String s = unit.toString().toLowerCase(Locale.US);
+        return s.substring(0, s.length() - 1);
+    }
+
+    private static class MetricTimeUnits {
+        private final TimeUnit defaultRate;
+        private final TimeUnit defaultDuration;
+        private final Map<String, TimeUnit> rateOverrides;
+        private final Map<String, TimeUnit> durationOverrides;
+
+        MetricTimeUnits(TimeUnit defaultRate,
+                        TimeUnit defaultDuration,
+                        Map<String, TimeUnit> rateOverrides,
+                        Map<String, TimeUnit> durationOverrides) {
+            this.defaultRate = defaultRate;
+            this.defaultDuration = defaultDuration;
+            this.rateOverrides = rateOverrides;
+            this.durationOverrides = durationOverrides;
+        }
+
+        public TimeUnit durationFor(String name) {
+            return durationOverrides.containsKey(name) ? durationOverrides.get(name) : defaultDuration;
+        }
+
+        public TimeUnit rateFor(String name) {
+            return rateOverrides.containsKey(name) ? rateOverrides.get(name) : defaultRate;
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/metrics/internal/MetricsServiceImpl.java b/src/main/java/org/apache/sling/metrics/internal/MetricsServiceImpl.java
new file mode 100644
index 0000000..dea3ea2
--- /dev/null
+++ b/src/main/java/org/apache/sling/metrics/internal/MetricsServiceImpl.java
@@ -0,0 +1,199 @@
+/*
+ * 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.sling.metrics.internal;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.management.MBeanServer;
+
+import com.codahale.metrics.JmxReporter;
+import com.codahale.metrics.MetricRegistry;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.sling.metrics.Counter;
+import org.apache.sling.metrics.Histogram;
+import org.apache.sling.metrics.Meter;
+import org.apache.sling.metrics.Metric;
+import org.apache.sling.metrics.MetricsService;
+import org.apache.sling.metrics.Timer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+
+@Component
+public class MetricsServiceImpl implements MetricsService{
+    private final List<ServiceRegistration> regs = new ArrayList<ServiceRegistration>();
+    private final ConcurrentMap<String, Metric> metrics = new ConcurrentHashMap<String, Metric>();
+    private final MetricRegistry registry = new MetricRegistry();
+
+    @Reference
+    private MBeanServer server;
+
+    private JmxReporter reporter;
+
+    @Activate
+    private void activate(BundleContext context, Map<String, Object> config) {
+        //TODO Domain name should be based on calling bundle
+        //For that we can register ServiceFactory and make use of calling
+        //bundle symbolic name to determine the mapping
+
+        reporter = JmxReporter.forRegistry(registry)
+                .inDomain("org.apache.sling")
+                .registerWith(server)
+                .build();
+
+        final Dictionary<String, String> svcProps = new Hashtable<String, String>();
+        svcProps.put(Constants.SERVICE_DESCRIPTION, "Apache Sling Metrics Service");
+        svcProps.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
+        regs.add(context.registerService(MetricsService.class.getName(), this, svcProps));
+
+        final Dictionary<String, String> regProps = new Hashtable<String, String>();
+        regProps.put(Constants.SERVICE_DESCRIPTION, "Apache Sling Metrics Registry");
+        regProps.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
+        regProps.put("name", "sling");
+        regs.add(context.registerService(MetricRegistry.class.getName(), registry, regProps));
+    }
+
+    @Deactivate
+    private void deactivate() throws IOException {
+        for (ServiceRegistration reg : regs) {
+            reg.unregister();
+        }
+        regs.clear();
+
+        metrics.clear();
+
+        if (reporter != null) {
+            reporter.close();
+        }
+    }
+
+    @Override
+    public Timer timer(String name) {
+        return getOrAdd(name, MetricBuilder.TIMERS);
+    }
+
+    @Override
+    public Histogram histogram(String name) {
+        return getOrAdd(name, MetricBuilder.HISTOGRAMS);
+    }
+
+    @Override
+    public Counter counter(String name) {
+        return getOrAdd(name, MetricBuilder.COUNTERS);
+    }
+
+    @Override
+    public Meter meter(String name) {
+        return getOrAdd(name, MetricBuilder.METERS);
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T extends Metric> T getOrAdd(String name, MetricBuilder<T> builder) {
+        final Metric metric = metrics.get(name);
+        if (builder.isInstance(metric)) {
+            return (T) metric;
+        } else if (metric == null) {
+            try {
+                return register(name, builder.newMetric(registry, name));
+            } catch (IllegalArgumentException e) {
+                final Metric added = metrics.get(name);
+                if (builder.isInstance(added)) {
+                    return (T) added;
+                }
+            }
+        }
+        throw new IllegalArgumentException(name + " is already used for a different type of metric");
+    }
+
+    private <T extends Metric> T register(String name, T metric) throws IllegalArgumentException {
+        final Metric existing = metrics.putIfAbsent(name, metric);
+        if (existing != null) {
+            throw new IllegalArgumentException("A metric named " + name + " already exists");
+        }
+        return metric;
+    }
+
+    /**
+     * A quick and easy way of capturing the notion of default metrics.
+     */
+    private interface MetricBuilder<T extends Metric> {
+        MetricBuilder<Counter> COUNTERS = new MetricBuilder<Counter>() {
+            @Override
+            public Counter newMetric(MetricRegistry registry, String name) {
+                return new CounterImpl(registry.counter(name));
+            }
+
+            @Override
+            public boolean isInstance(Metric metric) {
+                return Counter.class.isInstance(metric);
+            }
+        };
+
+        MetricBuilder<Histogram> HISTOGRAMS = new MetricBuilder<Histogram>() {
+            @Override
+            public Histogram newMetric(MetricRegistry registry, String name) {
+                return new HistogramImpl(registry.histogram(name));
+            }
+
+            @Override
+            public boolean isInstance(Metric metric) {
+                return Histogram.class.isInstance(metric);
+            }
+        };
+
+        MetricBuilder<Meter> METERS = new MetricBuilder<Meter>() {
+            @Override
+            public Meter newMetric(MetricRegistry registry, String name) {
+                return new MeterImpl(registry.meter(name));
+            }
+
+            @Override
+            public boolean isInstance(Metric metric) {
+                return Meter.class.isInstance(metric);
+            }
+        };
+
+        MetricBuilder<Timer> TIMERS = new MetricBuilder<Timer>() {
+            @Override
+            public Timer newMetric(MetricRegistry registry, String name) {
+                return new TimerImpl(registry.timer(name));
+            }
+
+            @Override
+            public boolean isInstance(Metric metric) {
+                return Timer.class.isInstance(metric);
+            }
+        };
+
+        T newMetric(MetricRegistry registry, String name);
+
+        boolean isInstance(Metric metric);
+    }
+}
diff --git a/src/main/java/org/apache/sling/metrics/internal/TimerImpl.java b/src/main/java/org/apache/sling/metrics/internal/TimerImpl.java
new file mode 100644
index 0000000..54fd39c
--- /dev/null
+++ b/src/main/java/org/apache/sling/metrics/internal/TimerImpl.java
@@ -0,0 +1,77 @@
+/*
+ * 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.sling.metrics.internal;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sling.metrics.Timer;
+
+
+final class TimerImpl implements Timer {
+    private final com.codahale.metrics.Timer timer;
+
+    TimerImpl(com.codahale.metrics.Timer timer) {
+        this.timer = timer;
+    }
+
+    @Override
+    public void update(long duration, TimeUnit unit) {
+        timer.update(duration, unit);
+    }
+
+    @Override
+    public Context time() {
+        return new ContextImpl(timer.time());
+    }
+
+    @Override
+    public long getCount() {
+        return timer.getCount();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <A> A adaptTo(Class<A> type) {
+        if (type == com.codahale.metrics.Timer.class) {
+            return (A) timer;
+        }
+        return null;
+    }
+
+    private static final class ContextImpl implements Context {
+        private final com.codahale.metrics.Timer.Context context;
+
+        private ContextImpl(com.codahale.metrics.Timer.Context context) {
+            this.context = context;
+        }
+
+        public long stop() {
+            return context.stop();
+        }
+
+        /**
+         * Equivalent to calling {@link #stop()}.
+         */
+        @Override
+        public void close() {
+            stop();
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/metrics/package-info.java b/src/main/java/org/apache/sling/metrics/package-info.java
new file mode 100644
index 0000000..9b4ae51
--- /dev/null
+++ b/src/main/java/org/apache/sling/metrics/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+/**
+ * Provides configuration support for the Logback based logging in Sling
+ *
+ * @version 1.0
+ */
+@Version("1.0")
+@Export(optional = "provide:=true")
+package org.apache.sling.metrics;
+
+import aQute.bnd.annotation.Export;
+import aQute.bnd.annotation.Version;
+
diff --git a/src/test/java/org/apache/sling/metrics/internal/MetricServiceTest.java b/src/test/java/org/apache/sling/metrics/internal/MetricServiceTest.java
new file mode 100644
index 0000000..e62daad
--- /dev/null
+++ b/src/test/java/org/apache/sling/metrics/internal/MetricServiceTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.sling.metrics.internal;
+
+import java.lang.management.ManagementFactory;
+import java.util.Collections;
+
+import javax.management.MBeanServer;
+
+import com.codahale.metrics.MetricRegistry;
+import org.apache.sling.metrics.Counter;
+import org.apache.sling.metrics.Histogram;
+import org.apache.sling.metrics.Meter;
+import org.apache.sling.metrics.MetricsService;
+import org.apache.sling.metrics.Timer;
+import org.apache.sling.testing.mock.osgi.MockOsgi;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+public class MetricServiceTest {
+    @Rule
+    public final OsgiContext context = new OsgiContext();
+
+    private MetricsServiceImpl service = new MetricsServiceImpl();
+
+    @After
+    public void registerMBeanServer() {
+        context.registerService(MBeanServer.class, ManagementFactory.getPlatformMBeanServer());
+    }
+
+    @Test
+    public void defaultSetup() throws Exception{
+        activate();
+
+        assertNotNull(context.getService(MetricRegistry.class));
+        assertNotNull(context.getService(MetricsService.class));
+
+        MockOsgi.deactivate(service);
+
+        assertNull(context.getService(MetricRegistry.class));
+        assertNull(context.getService(MetricsService.class));
+    }
+
+    @Test
+    public void meter() throws Exception{
+        activate();
+        Meter meter = service.meter("test");
+
+        assertNotNull(meter);
+        assertTrue(getRegistry().getMeters().containsKey("test"));
+
+        assertSame(meter, service.meter("test"));
+    }
+
+    @Test
+    public void counter() throws Exception{
+        activate();
+        Counter counter = service.counter("test");
+
+        assertNotNull(counter);
+        assertTrue(getRegistry().getCounters().containsKey("test"));
+
+        assertSame(counter, service.counter("test"));
+    }
+
+    @Test
+    public void timer() throws Exception{
+        activate();
+        Timer timer = service.timer("test");
+
+        assertNotNull(timer);
+        assertTrue(getRegistry().getTimers().containsKey("test"));
+
+        assertSame(timer, service.timer("test"));
+    }
+
+    @Test
+    public void histogram() throws Exception{
+        activate();
+        Histogram histo = service.histogram("test");
+
+        assertNotNull(histo);
+        assertTrue(getRegistry().getHistograms().containsKey("test"));
+
+        assertSame(histo, service.histogram("test"));
+    }
+
+    private MetricRegistry getRegistry(){
+        return context.getService(MetricRegistry.class);
+    }
+
+    private void activate() {
+        MockOsgi.activate(service, context.bundleContext(), Collections.<String, Object>emptyMap());
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/metrics/internal/MetricWrapperTest.java b/src/test/java/org/apache/sling/metrics/internal/MetricWrapperTest.java
new file mode 100644
index 0000000..898626c
--- /dev/null
+++ b/src/test/java/org/apache/sling/metrics/internal/MetricWrapperTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.sling.metrics.internal;
+
+import java.util.concurrent.TimeUnit;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.ExponentiallyDecayingReservoir;
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Timer;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+public class MetricWrapperTest {
+    private MetricRegistry registry = new MetricRegistry();
+
+    @Test
+    public void counter() throws Exception {
+        Counter counter = registry.counter("test");
+        CounterImpl counterStats = new CounterImpl(counter);
+
+        counterStats.inc();
+        assertEquals(1, counterStats.getCount());
+        assertEquals(1, counter.getCount());
+        assertEquals(1, counterStats.getCount());
+
+        counterStats.inc();
+        counterStats.inc();
+        assertEquals(3, counterStats.getCount());
+
+        counterStats.dec();
+        assertEquals(2, counterStats.getCount());
+        assertEquals(2, counter.getCount());
+
+        counterStats.inc(7);
+        assertEquals(9, counterStats.getCount());
+        assertEquals(9, counter.getCount());
+
+        counterStats.dec(5);
+        assertEquals(4, counterStats.getCount());
+        assertEquals(4, counter.getCount());
+
+        assertSame(counter, counterStats.adaptTo(Counter.class));
+    }
+
+    @Test
+    public void meter() throws Exception {
+        Meter meter = registry.meter("test");
+        MeterImpl meterStats = new MeterImpl(meter);
+
+        meterStats.mark();
+        assertEquals(1, meterStats.getCount());
+        assertEquals(1, meter.getCount());
+
+        meterStats.mark(5);
+        assertEquals(6, meterStats.getCount());
+        assertEquals(6, meter.getCount());
+        assertSame(meter, meterStats.adaptTo(Meter.class));
+    }
+
+    @Test
+    public void timer() throws Exception {
+        Timer time = registry.timer("test");
+        TimerImpl timerStats = new TimerImpl(time);
+
+        timerStats.update(100, TimeUnit.SECONDS);
+        assertEquals(1, time.getCount());
+        assertEquals(TimeUnit.SECONDS.toNanos(100), time.getSnapshot().getMax());
+
+        timerStats.update(100, TimeUnit.SECONDS);
+        assertEquals(2, timerStats.getCount());
+
+        assertSame(time, timerStats.adaptTo(Timer.class));
+    }
+
+    @Test
+    public void histogram() throws Exception {
+        Histogram histo = registry.histogram("test");
+        HistogramImpl histoStats = new HistogramImpl(histo);
+
+        histoStats.update(100);
+        assertEquals(1, histo.getCount());
+        assertEquals(1, histoStats.getCount());
+        assertEquals(100, histo.getSnapshot().getMax());
+
+        assertSame(histo, histoStats.adaptTo(Histogram.class));
+    }
+
+    @Test
+    public void timerContext() throws Exception{
+        VirtualClock clock = new VirtualClock();
+        Timer time = new Timer(new ExponentiallyDecayingReservoir(), clock);
+
+        TimerImpl timerStats = new TimerImpl(time);
+        org.apache.sling.metrics.Timer.Context context = timerStats.time();
+
+        clock.tick = TimeUnit.SECONDS.toNanos(314);
+        context.close();
+
+        assertEquals(1, time.getCount());
+        assertEquals(TimeUnit.SECONDS.toNanos(314), time.getSnapshot().getMax());
+    }
+
+    private static class VirtualClock extends com.codahale.metrics.Clock {
+        long tick;
+        @Override
+        public long getTick() {
+            return tick;
+        }
+    }
+}