Index: src/test/java/org/apache/cocoon/monitoring/statistics/StatisticsCollectorTest.java
===================================================================
--- src/test/java/org/apache/cocoon/monitoring/statistics/StatisticsCollectorTest.java	(revision 0)
+++ src/test/java/org/apache/cocoon/monitoring/statistics/StatisticsCollectorTest.java	(revision 0)
@@ -0,0 +1,212 @@
+/*
+ * 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.cocoon.monitoring.statistics;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Map;
+
+import org.junit.Test;
+
+public class StatisticsCollectorTest {
+
+    private static final String testKey1 = "test1";
+    private static final String testKey2 = "test2";
+    private static final String testKey3 = "test3";
+
+    @Test
+    public void testGetHitCount() {
+        StatisticsCollector collector = new StatisticsCollector();
+        collector.incerementCounter(testKey1);
+        assertEquals(1, collector.getHitCount(testKey1), 0);
+
+        collector.incerementCounter(testKey1);
+        assertEquals(2, collector.getHitCount(testKey1), 0);
+
+        collector.incerementCounter(testKey2);
+        assertEquals(1, collector.getHitCount(testKey2), 0);
+
+        collector.incerementCounter(testKey2);
+        assertEquals(2, collector.getHitCount(testKey2), 0);
+
+        collector.incerementCounter(testKey1);
+        assertEquals(3, collector.getHitCount(testKey1), 0);
+    }
+
+    @Test
+    public void testGetAllHitCount() {
+        StatisticsCollector collector = new StatisticsCollector();
+        collector.incerementCounter(testKey1);
+        assertEquals(1, collector.getAllHitCountSum(), 0);
+
+        collector.incerementCounter(testKey1);
+        assertEquals(2, collector.getAllHitCountSum(), 0);
+
+        collector.incerementCounter(testKey2);
+        assertEquals(3, collector.getAllHitCountSum(), 0);
+
+        collector.incerementCounter(testKey2);
+        assertEquals(4, collector.getAllHitCountSum(), 0);
+
+        collector.incerementCounter(testKey1);
+        assertEquals(5, collector.getAllHitCountSum(), 0);
+    }
+
+    @Test
+    public void testGetHitCountList() {
+        StatisticsCollector collector = new StatisticsCollector(100000, 100000);
+
+        collector.incerementCounter(testKey1);
+        collector.incerementCounter(testKey1);
+        collector.incerementCounter(testKey1);
+
+        collector.incerementCounter(testKey2);
+
+        sleep(60);
+
+        collector.incerementCounter(testKey3);
+        collector.incerementCounter(testKey3);
+        collector.incerementCounter(testKey3);
+        collector.incerementCounter(testKey3);
+
+        collector.incerementCounter(testKey2);
+
+        Map<String, Long> timeOut50ms = collector.getHitCountList(50);
+        Map<String, Long> timeOut70ms = collector.getHitCountList(70);
+
+        assertEquals(3, timeOut50ms.size());
+
+        assertTrue(timeOut50ms.containsKey(testKey3));
+        assertEquals(4, timeOut50ms.get(testKey3).longValue());
+
+        assertTrue(timeOut50ms.containsKey(testKey2));
+        assertEquals(1, timeOut50ms.get(testKey2).longValue());
+
+        assertTrue(timeOut50ms.containsKey(testKey1));
+        assertEquals(0, timeOut50ms.get(testKey1).longValue());
+
+        assertEquals(3, timeOut50ms.size());
+
+        assertTrue(timeOut70ms.containsKey(testKey3));
+        assertEquals(4, timeOut70ms.get(testKey3).longValue());
+
+        assertTrue(timeOut70ms.containsKey(testKey1));
+        assertEquals(3, timeOut70ms.get(testKey1).longValue());
+
+        assertTrue(timeOut70ms.containsKey(testKey2));
+        assertEquals(2, timeOut70ms.get(testKey2).longValue());
+
+    }
+
+    @Test
+    public void testGetRequestCount() {
+        StatisticsCollector collector = new StatisticsCollector(1000, 1000);
+
+        collector.incerementCounter(testKey1);
+
+        sleep(10);
+        assertEquals(1, collector.getRequestCount(testKey1, 50), 0);
+
+        sleep(300);
+        collector.incerementCounter(testKey1);
+        assertEquals(1, collector.getRequestCount(testKey1, 50), 0);
+        assertEquals(2, collector.getRequestCount(testKey1, 500), 0);
+
+        sleep(900);
+        collector.incerementCounter(testKey1);
+        assertEquals(1, collector.getRequestCount(testKey1, 50), 0);
+        assertEquals(1, collector.getRequestCount(testKey1, 500), 0);
+        assertEquals(2, collector.getRequestCount(testKey1, 1000), 0);
+
+        sleep(100);
+        assertEquals(1, collector.getRequestCount(testKey1, 1000), 0);
+
+        // wait for cleaning action
+        sleep(1000);
+
+        // check that everything was cleaned
+        assertEquals(0, collector.getRequestCount(testKey1, 50), 0);
+        assertEquals(0, collector.getRequestCount(testKey1, 500), 0);
+        assertEquals(0, collector.getRequestCount(testKey1, 1000), 0);
+
+    }
+
+    @Test
+    public void testIncerementCounter() {
+        StatisticsCollector collector = new StatisticsCollector();
+        collector.incerementCounter(testKey1);
+        assertEquals(1, collector.getAllHitCountSum(), 0);
+
+        Map<String, Long> map = collector.getHitCountList(100);
+
+        assertEquals(1, map.size());
+        assertTrue(map.containsKey(testKey1));
+        assertEquals(1, map.get(testKey1).longValue());
+    }
+
+    @Test
+    public void testGetAllHitCountMap() {
+        StatisticsCollector collector = new StatisticsCollector();
+
+        collector.incerementCounter(testKey1);
+        collector.incerementCounter(testKey2);
+        collector.incerementCounter(testKey3);
+        collector.incerementCounter(testKey1);
+        collector.incerementCounter(testKey2);
+        collector.incerementCounter(testKey1);
+
+        Map<String, Double> allHitMap = collector.getAllHitCountMap();
+
+        assertEquals(3, allHitMap.size());
+
+        assertTrue(allHitMap.containsKey(testKey1));
+        assertTrue(allHitMap.containsKey(testKey2));
+        assertTrue(allHitMap.containsKey(testKey3));
+
+        assertEquals(3, allHitMap.get(testKey1), 0);
+        assertEquals(2, allHitMap.get(testKey2), 0);
+        assertEquals(1, allHitMap.get(testKey3), 0);
+
+        allHitMap.put(testKey1, 40d);
+        assertNotSame(40, collector.getAllHitCountMap().get(testKey1));
+    }
+
+    @Test
+    public void testPutKey() {
+        StatisticsCollector collector = new StatisticsCollector();
+        collector.putKey(testKey1);
+
+        assertEquals(0, collector.getAllHitCountSum(), 0);
+        assertEquals(1, collector.getHitCountList(100).size(), 0);
+
+        assertEquals(0, collector.getRequestCount(testKey1, 100));
+        assertEquals(0, collector.getHitCountList(100).get(testKey1).floatValue(), 0);
+    }
+
+    private void sleep(long time) {
+        try {
+            Thread.sleep(time);
+        } catch (final InterruptedException e) {
+            throw new RuntimeException("Should never happens!");
+        }
+    }
+
+}
Index: src/main/java/org/apache/cocoon/monitoring/statistics/StatisticsCollector.java
===================================================================
--- src/main/java/org/apache/cocoon/monitoring/statistics/StatisticsCollector.java	(revision 0)
+++ src/main/java/org/apache/cocoon/monitoring/statistics/StatisticsCollector.java	(revision 0)
@@ -0,0 +1,191 @@
+/*
+ * 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.cocoon.monitoring.statistics;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class StatisticsCollector implements StatisticsSourceEnabled {
+
+    /**
+    * Default refresh time: 10s;
+    */
+    public static final long DEFAULT_REFRESH_DELAY = 1000 * 10;
+
+    /**
+    * Default value of maxKeepTime: 24h.
+    */
+    public static final long DEFAULT_MAX_KEEP_TIME = 1000 * 60 * 60 * 24;
+
+    private final Map<String, Double> allHitCount;
+    private final Map<String, List<Long>> coutners;
+    private final long maxKeepTime;
+
+    /**
+     * This constructor uses default values of {@link StatisticsCollector#DEFAULT_MAX_KEEP_TIME DEFAULT_MAX_KEEP_TIME},
+     * and {@link StatisticsCollector#DEFAULT_REFRESH_DELAY DEFAULT_REFRESH_DELAY} to pass into
+     * {@link StatisticsCollector#StatisticsCollector(long, long) StatisticsCollector(long, long)}
+     */
+    public StatisticsCollector() {
+        this(DEFAULT_MAX_KEEP_TIME, DEFAULT_REFRESH_DELAY);
+    }
+
+    /**
+     *
+     * @param maxKeepTime how long (in milliseconds) should be statistics data kept in collector
+     * @param refreshDelay delay time (in milliseconds) between run of cleaning thread, that will remove entry's
+     *          are older than value in <code>maxKeepTime</code>
+     */
+    public StatisticsCollector(long maxKeepTime, long refreshDelay) {
+        this.maxKeepTime = maxKeepTime;
+        this.allHitCount = Collections.synchronizedMap(new HashMap<String, Double>());
+        this.coutners = Collections.synchronizedMap(new HashMap<String, List<Long>>());
+
+        initCleaningThread(refreshDelay);
+    }
+
+    /** @{inheritDoc} */
+    public Map<String, Double> getAllHitCountMap() {
+        return new HashMap<String, Double>(this.allHitCount); // defense copy
+    }
+
+    /** @{inheritDoc} */
+    public Map<String, Long> getHitCountList(long time) {
+        final Map<String, Long> result = new HashMap<String, Long>();
+        long timeBorder = new Date().getTime() - time;
+
+        for (String key : this.coutners.keySet()) {
+            long sum = 0;
+            for (long item : this.coutners.get(key)) {
+                if (item > timeBorder) {
+                    sum++;
+                }
+            }
+            result.put(key, sum);
+        }
+
+        return result;
+    }
+
+    /** @{inheritDoc} */
+    public double getHitCount(String key) {
+        return this.allHitCount.containsKey(key) ? this.allHitCount.get(key) : 0;
+    }
+
+    /** @{inheritDoc} */
+    public double getAllHitCountSum() {
+        double result = 0;
+        for (Double count : this.allHitCount.values()) {
+            result += count;
+        }
+        return result;
+    }
+
+    /** @{inheritDoc} */
+    public long getRequestCount(String key, long time) {
+        if (!this.coutners.containsKey(key)) {
+            return 0;
+        }
+
+        List<Long> counter = this.coutners.get(key);
+
+        long hitCount = 0;
+        long currentTimestamp = new Date().getTime() - time;
+
+        for (Long timestamp : counter) {
+            if (timestamp > 0 && currentTimestamp < timestamp) {
+                hitCount++;
+            }
+        }
+
+        return hitCount;
+    }
+
+    /**
+     * Only adds key into counter but don't increment hit count for this
+     * <code>key</code>. It is useful if you want to have this <code>key</code> in a list
+     * of all used key's with value <strong>0</strong> (i.e. for registered but never used
+     * cache entry's).
+     *
+     * @param key
+     */
+    public void putKey(String key) {
+        insertDataIntoCounter(key, -1l);
+    }
+
+    /**
+     * Increment value of counter for particular <code>key</code>.
+     *
+     * <p>In fact this method adds actual time (in milliseconds) into list that is connected with
+     * this <code>key</code>.
+     *
+     * @param key
+     */
+    public void incerementCounter(String key) {
+        insertDataIntoCounter(key, new Date().getTime());
+    }
+
+    private void insertDataIntoCounter(String key, long data) {
+        if (!this.coutners.containsKey(key)) {
+            List<Long> list = new ArrayList<Long>();
+            list.add(data);
+            this.coutners.put(key, list);
+        } else {
+            this.coutners.get(key).add(data);
+        }
+
+        if (this.allHitCount.containsKey(key) && data > 0) {
+            this.allHitCount.put(key, this.allHitCount.get(key) + 1);
+        } else if (data > 0) {
+            this.allHitCount.put(key, 1d);
+        } else {
+            this.allHitCount.put(key, 0d);
+        }
+    }
+
+    private void initCleaningThread(long refreshDelay) {
+        Timer cleaningTimer = new Timer("RequestCounterCleaningTask", true);
+        cleaningTimer.scheduleAtFixedRate(new TimerTask() {
+
+            @Override
+            public void run() {
+                List<Long> toRemove = new ArrayList<Long>();
+                long currentTimestamp = new Date().getTime();
+
+                for (List<Long> counter : StatisticsCollector.this.coutners.values()) {
+                    for (Long timestamp : counter) {
+                        if (timestamp > 0 && currentTimestamp - timestamp > StatisticsCollector.this.maxKeepTime) {
+                            toRemove.add(timestamp);
+                        }
+                    }
+                    counter.removeAll(toRemove);
+                    toRemove.clear();
+                }
+
+            }
+        }, refreshDelay, refreshDelay);
+    }
+
+}
Index: src/main/java/org/apache/cocoon/monitoring/statistics/aspects/UrlHitCountStatisticsAspect.java
===================================================================
--- src/main/java/org/apache/cocoon/monitoring/statistics/aspects/UrlHitCountStatisticsAspect.java	(revision 0)
+++ src/main/java/org/apache/cocoon/monitoring/statistics/aspects/UrlHitCountStatisticsAspect.java	(revision 0)
@@ -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.cocoon.monitoring.statistics.aspects;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.cocoon.monitoring.statistics.StatisticsCollector;
+import org.apache.cocoon.monitoring.statistics.StatisticsEnabled;
+import org.apache.cocoon.monitoring.statistics.StatisticsSourceEnabled;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+
+@Aspect
+public class UrlHitCountStatisticsAspect implements StatisticsEnabled {
+
+    private final StatisticsCollector collector;
+
+    public UrlHitCountStatisticsAspect() {
+        this(StatisticsCollector.DEFAULT_MAX_KEEP_TIME, StatisticsCollector.DEFAULT_REFRESH_DELAY);
+    }
+
+    public UrlHitCountStatisticsAspect(long maxKeepTime, long refreshDelaty) {
+        this.collector = new StatisticsCollector(maxKeepTime, refreshDelaty);
+    }
+
+    @Around("execution(* service(..)) && target(javax.servlet.Servlet) && args(req, ..)")
+    public Object handleUrlRequest(ProceedingJoinPoint pjp, HttpServletRequest req) throws Throwable {
+        String className = pjp.getTarget().getClass().getName();
+        if (!className.startsWith("$Proxy")) {
+            this.collector.incerementCounter(req.getRequestURI());
+        }
+        return pjp.proceed(pjp.getArgs());
+    }
+
+    public StatisticsSourceEnabled getStatistics() {
+        return this.collector;
+    }
+
+    public String statisticsSourceName() {
+        return "UrlHitCount";
+    }
+}
Index: src/main/java/org/apache/cocoon/monitoring/statistics/aspects/ServletHitCountStatisticsAspect.java
===================================================================
--- src/main/java/org/apache/cocoon/monitoring/statistics/aspects/ServletHitCountStatisticsAspect.java	(revision 0)
+++ src/main/java/org/apache/cocoon/monitoring/statistics/aspects/ServletHitCountStatisticsAspect.java	(revision 0)
@@ -0,0 +1,55 @@
+/*
+ * 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.cocoon.monitoring.statistics.aspects;
+
+import org.apache.cocoon.monitoring.statistics.StatisticsCollector;
+import org.apache.cocoon.monitoring.statistics.StatisticsEnabled;
+import org.apache.cocoon.monitoring.statistics.StatisticsSourceEnabled;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+
+@Aspect
+public class ServletHitCountStatisticsAspect implements StatisticsEnabled {
+
+    private final StatisticsCollector collector;
+
+    public ServletHitCountStatisticsAspect() {
+        this(StatisticsCollector.DEFAULT_MAX_KEEP_TIME, StatisticsCollector.DEFAULT_REFRESH_DELAY);
+    }
+
+    public ServletHitCountStatisticsAspect(long maxKeepTime, long refreshDelaty) {
+        this.collector = new StatisticsCollector(maxKeepTime, refreshDelaty);
+    }
+
+    @Around("execution(* service(..)) && target(javax.servlet.Servlet))")
+    public Object handleServletRequest(ProceedingJoinPoint pjp) throws Throwable {
+        String className = pjp.getTarget().getClass().getName();
+        if (!className.startsWith("$Proxy")) {
+            this.collector.incerementCounter(className);
+        }
+        return pjp.proceed(pjp.getArgs());
+    }
+
+    public StatisticsSourceEnabled getStatistics() {
+        return this.collector;
+    }
+
+    public String statisticsSourceName() {
+        return "ServletHitCount";
+    }
+}
Index: src/main/java/org/apache/cocoon/monitoring/statistics/aspects/PipelineHitCountStatisticsAspect.java
===================================================================
--- src/main/java/org/apache/cocoon/monitoring/statistics/aspects/PipelineHitCountStatisticsAspect.java	(revision 0)
+++ src/main/java/org/apache/cocoon/monitoring/statistics/aspects/PipelineHitCountStatisticsAspect.java	(revision 0)
@@ -0,0 +1,44 @@
+/*
+ * 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.cocoon.monitoring.statistics.aspects;
+
+import org.apache.cocoon.monitoring.statistics.StatisticsCollector;
+import org.apache.cocoon.monitoring.statistics.StatisticsEnabled;
+import org.apache.cocoon.monitoring.statistics.StatisticsSourceEnabled;
+import org.aspectj.lang.annotation.Aspect;
+
+@Aspect
+public class PipelineHitCountStatisticsAspect implements StatisticsEnabled {
+
+    private final StatisticsCollector collector;
+
+    public PipelineHitCountStatisticsAspect() {
+        this(StatisticsCollector.DEFAULT_MAX_KEEP_TIME, StatisticsCollector.DEFAULT_REFRESH_DELAY);
+    }
+
+    public PipelineHitCountStatisticsAspect(long maxKeepTime, long refreshDelay) {
+        this.collector = new StatisticsCollector(maxKeepTime, refreshDelay);
+    }
+
+    public StatisticsSourceEnabled getStatistics() {
+        return this.collector;
+    }
+
+    public String statisticsSourceName() {
+        return "PipeLineHitCount";
+    }
+}
Index: src/main/java/org/apache/cocoon/monitoring/statistics/aspects/CacheStatisticsAspect.java
===================================================================
--- src/main/java/org/apache/cocoon/monitoring/statistics/aspects/CacheStatisticsAspect.java	(revision 0)
+++ src/main/java/org/apache/cocoon/monitoring/statistics/aspects/CacheStatisticsAspect.java	(revision 0)
@@ -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.cocoon.monitoring.statistics.aspects;
+
+import org.apache.cocoon.monitoring.statistics.StatisticsCollector;
+import org.apache.cocoon.monitoring.statistics.StatisticsEnabled;
+import org.apache.cocoon.monitoring.statistics.StatisticsSourceEnabled;
+import org.apache.cocoon.pipeline.caching.CacheKey;
+import org.aspectj.lang.annotation.After;
+import org.aspectj.lang.annotation.Aspect;
+
+@Aspect
+public class CacheStatisticsAspect implements StatisticsEnabled {
+
+    private final StatisticsCollector collector;
+
+    public CacheStatisticsAspect() {
+        this(StatisticsCollector.DEFAULT_MAX_KEEP_TIME, StatisticsCollector.DEFAULT_REFRESH_DELAY);
+    }
+
+    public CacheStatisticsAspect(long maxKeepTime, long refreshDelay) {
+        this.collector = new StatisticsCollector(maxKeepTime, refreshDelay);
+    }
+
+    @After("execution(* put(..)) && target(org.apache.cocoon.pipeline.caching.Cache) && args(key, ..)")
+    public void handleCachePutRequest(CacheKey key) throws Throwable {
+        this.collector.putKey(key.toString());
+    }
+
+    @After("execution(* get(..)) && target(org.apache.cocoon.pipeline.caching.Cache) && args(key, ..)")
+    public void handleCacheGetRequest(CacheKey key) throws Throwable {
+        this.collector.incerementCounter(key.toString());
+    }
+
+    public StatisticsSourceEnabled getStatistics() {
+        return this.collector;
+    }
+
+    public String statisticsSourceName() {
+        return "CacheHitCount";
+    }
+
+}
Index: src/main/java/org/apache/cocoon/monitoring/statistics/StatisticsEnabled.java
===================================================================
--- src/main/java/org/apache/cocoon/monitoring/statistics/StatisticsEnabled.java	(revision 0)
+++ src/main/java/org/apache/cocoon/monitoring/statistics/StatisticsEnabled.java	(revision 0)
@@ -0,0 +1,27 @@
+/*
+ * 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.cocoon.monitoring.statistics;
+
+public interface StatisticsEnabled {
+
+    String statisticsSourceName();
+
+    StatisticsSourceEnabled getStatistics();
+
+}
Index: src/main/java/org/apache/cocoon/monitoring/statistics/StatisticsSourceEnabled.java
===================================================================
--- src/main/java/org/apache/cocoon/monitoring/statistics/StatisticsSourceEnabled.java	(revision 0)
+++ src/main/java/org/apache/cocoon/monitoring/statistics/StatisticsSourceEnabled.java	(revision 0)
@@ -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,in ascending order
+ * 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.cocoon.monitoring.statistics;
+
+import java.util.Map;
+
+public interface StatisticsSourceEnabled {
+
+    /**
+     * Returns sum for all hit count for given key. It will return <strong>0<strong> if given key does not
+     * have any statistics data (that means, that <strong>0</strong> if given key does not exist in statistics
+     * source).
+     *
+     * @param key
+     * @return hit count for passed arguments
+     */
+    public double getHitCount(String key);
+
+    /**
+     * Returns all hit count for this statistics source (sum of all hit counts for all contained key's).
+     *
+     * @return
+     */
+    double getAllHitCountSum();
+
+    /**
+     * Returns unordered {@link Map} of summed value of all hit
+     *
+     * @return
+     */
+    Map<String, Double> getAllHitCountMap();
+
+    /**
+     * Returns unordered {@link Map}, where <code>key</code> is source name and <code>value</code> is
+     * sum of hit count, limited only to entry's that are younger then <code>time</code> parameter
+     *
+     * @param time
+     * @return
+     */
+    Map<String, Long> getHitCountList(long time);
+
+    /**
+     * Returns hit count for particular <code>key</code> in particular <code>time</code>
+     *
+     * @param key
+     * @param time
+     * @return
+     */
+    long getRequestCount(String key, long time);
+
+}
Index: src/main/java/org/apache/cocoon/monitoring/statistics/Statistics.java
===================================================================
--- src/main/java/org/apache/cocoon/monitoring/statistics/Statistics.java	(revision 0)
+++ src/main/java/org/apache/cocoon/monitoring/statistics/Statistics.java	(revision 0)
@@ -0,0 +1,108 @@
+/*
+ * 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.cocoon.monitoring.statistics;
+
+import java.util.Comparator;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.springframework.jmx.export.annotation.ManagedAttribute;
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedOperationParameter;
+import org.springframework.jmx.export.annotation.ManagedOperationParameters;
+import org.springframework.jmx.export.annotation.ManagedResource;
+
+@ManagedResource
+public class Statistics {
+
+    private final StatisticsSourceEnabled stats;
+
+    public Statistics(StatisticsEnabled stats) {
+        this.stats = stats.getStatistics();
+    }
+
+    @ManagedAttribute(description = "Returns all hit count since system start.")
+    public double getAllHitCount() {
+        return this.stats.getAllHitCountSum();
+    }
+
+    @ManagedAttribute(description = "Returns ascendent ordered list with sum of all hit count.")
+    public SortedMap<String, Double> getAllHitCountListAsc() {
+        final Map<String, Double> map = this.stats.getAllHitCountMap();
+
+        SortedMap<String, Double> sortedMap = new TreeMap<String, Double>(new SerializableComparator<String>() {
+            public int compare(String o1, String o2) {
+                return (int) (map.get(o2) - map.get(o1));
+            }
+        });
+
+        sortedMap.putAll(map);
+
+        return sortedMap;
+    }
+
+    @ManagedAttribute(description = "Returns descendent ordered list with sum of all hit count.")
+    public Map<String, Double> getAllHitCountListDesc() {
+        final Map<String, Double> map = this.stats.getAllHitCountMap();
+
+        SortedMap<String, Double> sortedMap = new TreeMap<String, Double>(new Comparator<String>() {
+            public int compare(String o1, String o2) {
+                return (int) (map.get(o1) - map.get(o2));
+            }
+        });
+
+        sortedMap.putAll(map);
+
+        return sortedMap;
+    }
+
+    @ManagedOperation(description = "Returns ascendent ordered list with sum of all hit count limited by time parameter.")
+    @ManagedOperationParameters( { @ManagedOperationParameter(name = "time", description = "Time in miliseconds. Limit data set in result only for entrys that was created ") })
+    public Map<String, Long> getHitCountListAsc(long time) {
+        final Map<String, Long> map = this.stats.getHitCountList(time);
+
+        SortedMap<String, Long> sortedMap = new TreeMap<String, Long>(new Comparator<String>() {
+            public int compare(String o1, String o2) {
+                return (int) (map.get(o2) - map.get(o1));
+            }
+        });
+
+        sortedMap.putAll(map);
+
+        return sortedMap;
+    }
+
+    @ManagedOperation(description = "Returns ascendent ordered list with sum of all hit count limited by time parameter.")
+    @ManagedOperationParameters( { @ManagedOperationParameter(name = "time", description = "Time in miliseconds. Limit data set in result only for entrys that was created ") })
+    public Map<String, Long> getHitCountListDesc(long time) {
+        final Map<String, Long> map = this.stats.getHitCountList(time);
+
+        SortedMap<String, Long> sortedMap = new TreeMap<String, Long>(new Comparator<String>() {
+            public int compare(String o1, String o2) {
+                return (int) (map.get(o1) - map.get(o2));
+            }
+        });
+
+        sortedMap.putAll(map);
+
+        return sortedMap;
+    }
+
+}
Index: src/main/java/org/apache/cocoon/monitoring/statistics/SerializableComparator.java
===================================================================
--- src/main/java/org/apache/cocoon/monitoring/statistics/SerializableComparator.java	(revision 0)
+++ src/main/java/org/apache/cocoon/monitoring/statistics/SerializableComparator.java	(revision 0)
@@ -0,0 +1,26 @@
+/*
+ * 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.cocoon.monitoring.statistics;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+public interface SerializableComparator<T> extends Comparator<T>, Serializable {
+
+}
Index: src/main/java/org/apache/cocoon/monitoring/statistics/StatisticsInitializer.java
===================================================================
--- src/main/java/org/apache/cocoon/monitoring/statistics/StatisticsInitializer.java	(revision 0)
+++ src/main/java/org/apache/cocoon/monitoring/statistics/StatisticsInitializer.java	(revision 0)
@@ -0,0 +1,51 @@
+/*
+ * 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.cocoon.monitoring.statistics;
+
+import java.util.Map;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.jmx.export.MBeanExporter;
+
+public class StatisticsInitializer {
+
+    private final Log logger = LogFactory.getLog(this.getClass());
+
+    public StatisticsInitializer(Map<String, StatisticsEnabled> statsSources, MBeanExporter exporter) {
+        for (StatisticsEnabled stat : statsSources.values()) {
+            String stringName = "org.apache.cocoon.monitoring:group=Statistics,name=" + stat.statisticsSourceName();
+
+            ObjectName name;
+            try {
+                name = new ObjectName(stringName);
+            } catch (MalformedObjectNameException e) {
+                this.logger.error("Invalid name of manager resource: " + stringName, e);
+                continue;
+            } catch (NullPointerException e) {
+                this.logger.error("Should never happened. Value of name parameter always is different than null.", e);
+                continue;
+            }
+
+            exporter.registerManagedResource(new Statistics(stat), name);
+        }
+    }
+
+}
Index: src/main/resources/META-INF/cocoon/spring/cocoon-monitoring.xml
===================================================================
--- src/main/resources/META-INF/cocoon/spring/cocoon-monitoring.xml	(revision 830903)
+++ src/main/resources/META-INF/cocoon/spring/cocoon-monitoring.xml	(working copy)
@@ -71,4 +77,21 @@
     </constructor-arg>
     <constructor-arg ref="exporter" index="1" />
   </bean>
+  
+  <!--  configuration for statistics submodule -->
+  <!-- <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" /> -->
+  <bean id="org.apache.cocoon.monitoring.statistics.aspects.ServletHitCountStatisticsAspect" class="org.apache.cocoon.monitoring.statistics.aspects.ServletHitCountStatisticsAspect" />
+  
+  <bean id="org.apache.cocoon.monitoring.statistics.aspects.UrlHitCountStatisticsAspect" class="org.apache.cocoon.monitoring.statistics.aspects.UrlHitCountStatisticsAspect" />
+  
+  <bean id="org.apache.cocoon.monitoring.statistics.aspects.CacheStatisticsAspect" class="org.apache.cocoon.monitoring.statistics.aspects.CacheStatisticsAspect" />
+  
+  <bean id="org.apache.cocoon.monitoring.statistics.StatisticsInitializer" class="org.apache.cocoon.monitoring.statistics.StatisticsInitializer">
+    <constructor-arg index="0">
+      <configurator:bean-map type="org.apache.cocoon.monitoring.statistics.StatisticsEnabled" />
+    </constructor-arg>
+    <constructor-arg ref="exporter" index="1" />
+  </bean>
+  
+  <aop:aspectj-autoproxy/>
 </beans>
Index: pom.xml
===================================================================
--- pom.xml	(revision 830903)
+++ pom.xml	(working copy)
@@ -64,7 +64,18 @@
       <groupId>log4j</groupId>
       <artifactId>log4j</artifactId>
     </dependency>
-
+    <dependency>
+      <groupId>org.apache.cocoon.pipeline</groupId>
+      <artifactId>cocoon-pipeline</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-aop</artifactId>
+    </dependency>
     <!--  Test dependencies -->
     <dependency>
       <groupId>junit</groupId>
