diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryContext.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryContext.java
index 08cea43..e98cc8d 100644
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryContext.java
+++ b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryContext.java
@@ -34,7 +34,7 @@ import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
import org.apache.jackrabbit.core.security.JackrabbitSecurityManager;
import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
import org.apache.jackrabbit.core.state.ItemStateCacheFactory;
-import org.apache.jackrabbit.core.stats.RepositoryStatisticsImpl;
+import org.apache.jackrabbit.stats.RepositoryStatisticsImpl;
import org.apache.jackrabbit.core.stats.StatManager;
import org.apache.jackrabbit.core.version.InternalVersionManagerImpl;
diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/jmx/QueryStatManager.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/jmx/QueryStatManager.java
deleted file mode 100644
index 24abd2f..0000000
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/jmx/QueryStatManager.java
+++ /dev/null
@@ -1,139 +0,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.
- */
-package org.apache.jackrabbit.core.jmx;
-
-import javax.management.openmbean.CompositeDataSupport;
-import javax.management.openmbean.CompositeType;
-import javax.management.openmbean.OpenDataException;
-import javax.management.openmbean.OpenType;
-import javax.management.openmbean.SimpleType;
-import javax.management.openmbean.TabularData;
-import javax.management.openmbean.TabularDataSupport;
-import javax.management.openmbean.TabularType;
-
-import org.apache.jackrabbit.api.jmx.QueryStatManagerMBean;
-import org.apache.jackrabbit.api.stats.QueryStat;
-import org.apache.jackrabbit.api.stats.QueryStatDto;
-
-/**
- * The QueryStatManagerMBean default implementation
- *
- */
-public class QueryStatManager implements QueryStatManagerMBean {
-
- private final QueryStat queryStat;
-
- public QueryStatManager(final QueryStat queryStat) {
- this.queryStat = queryStat;
- }
-
- public boolean isEnabled() {
- return this.queryStat.isEnabled();
- }
-
- public void enable() {
- this.queryStat.setEnabled(true);
- }
-
- public void disable() {
- this.queryStat.setEnabled(false);
- }
-
- public void reset() {
- this.queryStat.reset();
- }
-
- public int getSlowQueriesQueueSize() {
- return queryStat.getSlowQueriesQueueSize();
- }
-
- public void setSlowQueriesQueueSize(int size) {
- this.queryStat.setSlowQueriesQueueSize(size);
- }
-
- public void clearSlowQueriesQueue() {
- this.queryStat.clearSlowQueriesQueue();
- }
-
- public int getPopularQueriesQueueSize() {
- return queryStat.getPopularQueriesQueueSize();
- }
-
- public void setPopularQueriesQueueSize(int size) {
- queryStat.setPopularQueriesQueueSize(size);
- }
-
- public void clearPopularQueriesQueue() {
- queryStat.clearPopularQueriesQueue();
- }
-
- public TabularData getSlowQueries() {
- return asTabularData(queryStat.getSlowQueries());
- }
-
- public TabularData getPopularQueries() {
- return asTabularData(queryStat.getPopularQueries());
- }
-
- private TabularData asTabularData(QueryStatDto[] data) {
- TabularDataSupport tds = null;
- try {
- CompositeType ct = QueryStatCompositeTypeFactory.getCompositeType();
-
- TabularType tt = new TabularType(QueryStatDto.class.getName(),
- "Query History", ct, QueryStatCompositeTypeFactory.index);
- tds = new TabularDataSupport(tt);
-
- for (QueryStatDto q : data) {
- tds.put(new CompositeDataSupport(ct,
- QueryStatCompositeTypeFactory.names,
- QueryStatCompositeTypeFactory.getValues(q)));
- }
- return tds;
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-
- private static class QueryStatCompositeTypeFactory {
-
- private final static String[] index = { "position" };
-
- private final static String[] names = { "position", "duration",
- "occurrenceCount", "language", "statement", "creationTime" };
-
- private final static String[] descriptions = { "position", "duration",
- "occurrenceCount", "language", "statement", "creationTime" };
-
- private final static OpenType[] types = { SimpleType.LONG,
- SimpleType.LONG, SimpleType.INTEGER, SimpleType.STRING,
- SimpleType.STRING, SimpleType.STRING };
-
- public static CompositeType getCompositeType() throws OpenDataException {
- return new CompositeType(QueryStat.class.getName(),
- QueryStat.class.getName(), names, descriptions, types);
- }
-
- public static Object[] getValues(QueryStatDto q) {
- return new Object[] { q.getPosition(), q.getDuration(),
- q.getOccurrenceCount(), q.getLanguage(), q.getStatement(),
- q.getCreationTime() };
- }
- }
-
-}
diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PMContext.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PMContext.java
index d890eb1..272bae8 100644
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PMContext.java
+++ b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PMContext.java
@@ -24,7 +24,7 @@ import org.apache.jackrabbit.core.data.DataStore;
import org.apache.jackrabbit.core.fs.FileSystem;
import org.apache.jackrabbit.core.id.NodeId;
import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
-import org.apache.jackrabbit.core.stats.RepositoryStatisticsImpl;
+import org.apache.jackrabbit.stats.RepositoryStatisticsImpl;
/**
* A PMContext is used to provide context information for a
diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java
index 8d7cb63..00c11e0 100644
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java
+++ b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java
@@ -59,7 +59,7 @@ import org.apache.jackrabbit.core.state.NoSuchItemStateException;
import org.apache.jackrabbit.core.state.NodeReferences;
import org.apache.jackrabbit.core.state.NodeState;
import org.apache.jackrabbit.core.state.PropertyState;
-import org.apache.jackrabbit.core.stats.RepositoryStatisticsImpl;
+import org.apache.jackrabbit.stats.RepositoryStatisticsImpl;
import org.apache.jackrabbit.core.util.StringIndex;
import org.apache.jackrabbit.core.value.InternalValue;
import org.apache.jackrabbit.spi.Name;
diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryImpl.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryImpl.java
index 13647aa..f65a8b4 100644
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryImpl.java
+++ b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryImpl.java
@@ -36,7 +36,7 @@ import javax.jcr.version.VersionException;
import org.apache.jackrabbit.api.stats.RepositoryStatistics.Type;
import org.apache.jackrabbit.core.session.SessionContext;
import org.apache.jackrabbit.core.session.SessionOperation;
-import org.apache.jackrabbit.core.stats.RepositoryStatisticsImpl;
+import org.apache.jackrabbit.stats.RepositoryStatisticsImpl;
import org.apache.jackrabbit.spi.Path;
import org.apache.jackrabbit.spi.commons.conversion.NameException;
import org.slf4j.Logger;
diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryObjectModelImpl.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryObjectModelImpl.java
index 33ec2fb..fa27dc7 100644
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryObjectModelImpl.java
+++ b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryObjectModelImpl.java
@@ -37,7 +37,7 @@ import org.apache.jackrabbit.core.query.lucene.SearchIndex;
import org.apache.jackrabbit.core.query.lucene.join.QueryEngine;
import org.apache.jackrabbit.core.session.SessionContext;
import org.apache.jackrabbit.core.session.SessionOperation;
-import org.apache.jackrabbit.core.stats.RepositoryStatisticsImpl;
+import org.apache.jackrabbit.stats.RepositoryStatisticsImpl;
import org.apache.jackrabbit.spi.commons.query.qom.BindVariableValueImpl;
import org.apache.jackrabbit.spi.commons.query.qom.DefaultTraversingQOMTreeVisitor;
import org.apache.jackrabbit.spi.commons.query.qom.QueryObjectModelTree;
diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/session/SessionState.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/session/SessionState.java
index bf0b655..d296368 100644
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/session/SessionState.java
+++ b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/session/SessionState.java
@@ -27,7 +27,7 @@ import javax.jcr.Session;
import org.apache.jackrabbit.core.WorkspaceManager;
import org.apache.jackrabbit.core.observation.ObservationDispatcher;
-import org.apache.jackrabbit.core.stats.RepositoryStatisticsImpl;
+import org.apache.jackrabbit.stats.RepositoryStatisticsImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/QueryStatCore.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/QueryStatCore.java
deleted file mode 100644
index 27be0f7..0000000
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/QueryStatCore.java
+++ /dev/null
@@ -1,40 +0,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.
- */
-package org.apache.jackrabbit.core.stats;
-
-import org.apache.jackrabbit.api.stats.QueryStat;
-
-/**
- * Extends external facing {@link QueryStat} with some internal operations
- *
- */
-public interface QueryStatCore extends QueryStat {
-
- /**
- * Logs the call of each query ran on the repository.
- *
- * @param language
- * the query language, see
- * {@link org.apache.jackrabbit.spi.commons.name.NameConstants#JCR_LANGUAGE}
- * @param statement
- * the query
- * @param durationMs
- * time in ms
- */
- void logQuery(final String language, final String statement, long durationMs);
-
-}
diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/QueryStatDtoComparator.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/QueryStatDtoComparator.java
deleted file mode 100644
index 31a4742..0000000
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/QueryStatDtoComparator.java
+++ /dev/null
@@ -1,31 +0,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.
- */
-package org.apache.jackrabbit.core.stats;
-
-import java.util.Comparator;
-
-import org.apache.jackrabbit.api.stats.QueryStatDto;
-
-/**
- * QueryStatDto comparator by duration
- *
- */
-public class QueryStatDtoComparator implements Comparator {
- public int compare(QueryStatDto o1, QueryStatDto o2) {
- return new Long(o1.getDuration()).compareTo(o2.getDuration());
- }
-}
diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/QueryStatDtoImpl.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/QueryStatDtoImpl.java
deleted file mode 100644
index 18699fe..0000000
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/QueryStatDtoImpl.java
+++ /dev/null
@@ -1,145 +0,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.
- */
-package org.apache.jackrabbit.core.stats;
-
-import java.util.Calendar;
-import java.util.Date;
-
-import org.apache.jackrabbit.api.stats.QueryStatDto;
-
-/**
- * Object that holds statistical info about a query.
- *
- */
-public class QueryStatDtoImpl implements QueryStatDto {
-
- private static final long serialVersionUID = 1L;
-
- /**
- * lazy, computed at call time
- */
- private long position;
-
- /**
- * the time that the query was created
- */
- private final Date creationTime;
-
- /**
- * run duration in ms
- */
- private final long durationMs;
-
- /**
- * query language
- */
- private final String language;
-
- /**
- * query statement
- */
- private final String statement;
-
- /**
- * used in popular queries list
- */
- private int occurrenceCount = 1;
-
- public QueryStatDtoImpl(final String language, final String statement,
- long durationMs) {
- this.durationMs = durationMs;
- this.language = language;
- this.statement = statement;
-
- Calendar c = Calendar.getInstance();
- c.setTimeInMillis(System.currentTimeMillis() - durationMs);
- this.creationTime = c.getTime();
- }
-
- public long getDuration() {
- return durationMs;
- }
-
- public String getLanguage() {
- return language;
- }
-
- public String getStatement() {
- return statement;
- }
-
- public String getCreationTime() {
- return creationTime.toString();
- }
-
- public long getPosition() {
- return position;
- }
-
- public void setPosition(long position) {
- this.position = position;
- }
-
- @Override
- public String toString() {
- return "QueryStat [creationTime=" + creationTime + ", duration="
- + durationMs + ", position " + position + ", language="
- + language + ", statement=" + statement + "]";
- }
-
- public int getOccurrenceCount() {
- return occurrenceCount;
- }
-
- public void setOccurrenceCount(int occurrenceCount) {
- this.occurrenceCount = occurrenceCount;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result
- + ((language == null) ? 0 : language.hashCode());
- result = prime * result
- + ((statement == null) ? 0 : statement.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- QueryStatDtoImpl other = (QueryStatDtoImpl) obj;
- if (language == null) {
- if (other.language != null)
- return false;
- } else if (!language.equals(other.language))
- return false;
- if (statement == null) {
- if (other.statement != null)
- return false;
- } else if (!statement.equals(other.statement))
- return false;
- return true;
- }
-
-}
diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/QueryStatDtoOccurrenceComparator.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/QueryStatDtoOccurrenceComparator.java
deleted file mode 100644
index 29c5be2..0000000
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/QueryStatDtoOccurrenceComparator.java
+++ /dev/null
@@ -1,33 +0,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.
- */
-package org.apache.jackrabbit.core.stats;
-
-import java.util.Comparator;
-
-/**
- * QueryStatDto comparator by occurrence count
- *
- * used by the popular queries queue
- *
- */
-public class QueryStatDtoOccurrenceComparator implements
- Comparator {
- public int compare(QueryStatDtoImpl o1, QueryStatDtoImpl o2) {
- return new Integer(o1.getOccurrenceCount()).compareTo(o2
- .getOccurrenceCount());
- }
-}
diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/QueryStatImpl.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/QueryStatImpl.java
deleted file mode 100644
index 3fd2c66..0000000
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/QueryStatImpl.java
+++ /dev/null
@@ -1,186 +0,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.
- */
-package org.apache.jackrabbit.core.stats;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.concurrent.PriorityBlockingQueue;
-
-import org.apache.jackrabbit.api.stats.QueryStatDto;
-
-/**
- * Default {@link QueryStatCore} implementation
- *
- */
-public class QueryStatImpl implements QueryStatCore {
-
- private final static Comparator comparator = new QueryStatDtoComparator();
-
- private final BoundedPriorityBlockingQueue slowQueries = new BoundedPriorityBlockingQueue(
- 15, comparator);
-
- private final static Comparator comparatorOccurrence = new QueryStatDtoOccurrenceComparator();
-
- /**
- * the real queue size will be bigger than the desired number of popular
- * queries by POPULAR_QUEUE_MULTIPLIER times
- */
- private static final int POPULAR_QUEUE_MULTIPLIER = 5;
-
- private final BoundedPriorityBlockingQueue popularQueries = new BoundedPriorityBlockingQueue(
- 15 * POPULAR_QUEUE_MULTIPLIER, comparatorOccurrence);
-
- private static final class BoundedPriorityBlockingQueue extends
- PriorityBlockingQueue {
-
- private static final long serialVersionUID = 1L;
- private int maxSize;
-
- public BoundedPriorityBlockingQueue(int maxSize,
- Comparator super E> comparator) {
- super(maxSize + 1, comparator);
- this.maxSize = maxSize;
- }
-
- @Override
- public boolean offer(E e) {
- boolean s = super.offer(e);
- if (!s) {
- return false;
- }
- if (size() > maxSize) {
- poll();
- }
- return true;
- }
-
- public synchronized void setMaxSize(int maxSize) {
- if (maxSize < this.maxSize) {
- // shrink the queue
- int delta = super.size() - maxSize;
- for (int i = 0; i < delta; i++) {
- E t = poll();
- if (t == null) {
- break;
- }
- }
- }
- this.maxSize = maxSize;
- }
-
- public int getMaxSize() {
- return maxSize;
- }
- }
-
- private boolean enabled = false;
-
- public QueryStatImpl() {
- }
-
- public int getSlowQueriesQueueSize() {
- return slowQueries.getMaxSize();
- }
-
- public void setSlowQueriesQueueSize(int size) {
- slowQueries.setMaxSize(size);
- }
-
- public boolean isEnabled() {
- return enabled;
- }
-
- public synchronized void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public void logQuery(final String language, final String statement,
- long durationMs) {
- if (!enabled) {
- return;
- }
- final QueryStatDtoImpl qs = new QueryStatDtoImpl(language, statement,
- durationMs);
- slowQueries.offer(qs);
-
- synchronized (popularQueries) {
- Iterator iterator = popularQueries.iterator();
- while (iterator.hasNext()) {
- QueryStatDtoImpl qsdi = iterator.next();
- if (qsdi.equals(qs)) {
- qs.setOccurrenceCount(qsdi.getOccurrenceCount() + 1);
- iterator.remove();
- break;
- }
- }
- popularQueries.offer(qs);
- }
- }
-
- public void clearSlowQueriesQueue() {
- slowQueries.clear();
- }
-
- public QueryStatDto[] getSlowQueries() {
- QueryStatDto[] top = slowQueries.toArray(new QueryStatDto[slowQueries
- .size()]);
- Arrays.sort(top, Collections.reverseOrder(comparator));
- for (int i = 0; i < top.length; i++) {
- top[i].setPosition(i + 1);
- }
- return top;
- }
-
- public QueryStatDto[] getPopularQueries() {
- QueryStatDtoImpl[] top;
- int size = 0;
- int maxSize = 0;
- synchronized (popularQueries) {
- top = popularQueries.toArray(new QueryStatDtoImpl[popularQueries
- .size()]);
- size = popularQueries.size();
- maxSize = popularQueries.getMaxSize();
- }
- Arrays.sort(top, Collections.reverseOrder(comparatorOccurrence));
- int retSize = Math.min(size, maxSize / POPULAR_QUEUE_MULTIPLIER);
- QueryStatDto[] retval = new QueryStatDto[retSize];
- for (int i = 0; i < retSize; i++) {
- retval[i] = top[i];
- retval[i].setPosition(i + 1);
- }
- return retval;
- }
-
- public int getPopularQueriesQueueSize() {
- return popularQueries.getMaxSize() / POPULAR_QUEUE_MULTIPLIER;
- }
-
- public void setPopularQueriesQueueSize(int size) {
- popularQueries.setMaxSize(size * POPULAR_QUEUE_MULTIPLIER);
- }
-
- public void clearPopularQueriesQueue() {
- popularQueries.clear();
- }
-
- public void reset() {
- clearSlowQueriesQueue();
- clearPopularQueriesQueue();
- }
-}
diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/RepositoryStatisticsImpl.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/RepositoryStatisticsImpl.java
deleted file mode 100644
index 9bae45f..0000000
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/RepositoryStatisticsImpl.java
+++ /dev/null
@@ -1,114 +0,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.
- */
-package org.apache.jackrabbit.core.stats;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.TreeMap;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.apache.jackrabbit.api.stats.RepositoryStatistics;
-import org.apache.jackrabbit.api.stats.TimeSeries;
-
-public class RepositoryStatisticsImpl implements
- Iterable>, RepositoryStatistics {
-
- private final Map recorders =
- new HashMap();
-
- private final Map avg =
- new HashMap();
-
- public RepositoryStatisticsImpl() {
- getOrCreateRecorder(Type.SESSION_COUNT);
- getOrCreateRecorder(Type.SESSION_LOGIN_COUNTER);
-
- createAvg(Type.SESSION_READ_COUNTER, Type.SESSION_READ_DURATION,
- Type.SESSION_READ_AVERAGE);
- createAvg(Type.SESSION_WRITE_COUNTER, Type.SESSION_WRITE_DURATION,
- Type.SESSION_WRITE_AVERAGE);
- createAvg(Type.BUNDLE_CACHE_MISS_COUNTER,
- Type.BUNDLE_CACHE_MISS_DURATION, Type.BUNDLE_CACHE_MISS_AVERAGE);
- createAvg(Type.BUNDLE_WRITE_COUNTER, Type.BUNDLE_WRITE_DURATION,
- Type.BUNDLE_WRITE_AVERAGE);
- createAvg(Type.QUERY_COUNT, Type.QUERY_DURATION, Type.QUERY_AVERAGE);
-
- }
-
- private void createAvg(Type count, Type duration, Type avgTs) {
- avg.put(avgTs.name(), new TimeSeriesAverage(getOrCreateRecorder(duration),
- getOrCreateRecorder(count)));
- }
-
- public RepositoryStatisticsImpl(ScheduledExecutorService executor) {
- this();
- executor.scheduleAtFixedRate(new Runnable() {
- public void run() {
- recordOneSecond();
- }
- }, 1, 1, TimeUnit.SECONDS);
- }
-
- public synchronized Iterator> iterator() {
- Map map = new TreeMap();
- map.putAll(recorders);
- map.putAll(avg);
- return map.entrySet().iterator();
- }
-
- public AtomicLong getCounter(Type type) {
- return getOrCreateRecorder(type).getCounter();
- }
-
- public AtomicLong getCounter(String type, boolean resetValueEachSecond) {
- return getOrCreateRecorder(type, resetValueEachSecond).getCounter();
- }
-
- public TimeSeries getTimeSeries(Type type) {
- return getTimeSeries(type.name(), type.isResetValueEachSecond());
- }
-
- public TimeSeries getTimeSeries(String type, boolean resetValueEachSecond) {
- if (avg.containsKey(type)) {
- return avg.get(type);
- }
- return getOrCreateRecorder(type, resetValueEachSecond);
- }
-
- private synchronized TimeSeriesRecorder getOrCreateRecorder(Type type) {
- return getOrCreateRecorder(type.name(), type.isResetValueEachSecond());
- }
-
- private synchronized TimeSeriesRecorder getOrCreateRecorder(String type, boolean resetValueEachSecond) {
- TimeSeriesRecorder recorder = recorders.get(type);
- if (recorder == null) {
- recorder = new TimeSeriesRecorder(resetValueEachSecond);
- recorders.put(type, recorder);
- }
- return recorder;
- }
-
- private synchronized void recordOneSecond() {
- for (TimeSeriesRecorder recorder : recorders.values()) {
- recorder.recordOneSecond();
- }
- }
-}
diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/StatManager.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/StatManager.java
index 8b888cc..d71c017 100644
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/StatManager.java
+++ b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/StatManager.java
@@ -18,6 +18,8 @@ package org.apache.jackrabbit.core.stats;
import static java.lang.Boolean.getBoolean;
+import org.apache.jackrabbit.stats.QueryStatCore;
+import org.apache.jackrabbit.stats.QueryStatImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/TimeSeriesAverage.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/TimeSeriesAverage.java
deleted file mode 100644
index e8f66db..0000000
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/TimeSeriesAverage.java
+++ /dev/null
@@ -1,85 +0,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.
- */
-package org.apache.jackrabbit.core.stats;
-
-import org.apache.jackrabbit.api.stats.TimeSeries;
-
-/**
- * Time series of the average calculated by dividing a measured
- * value by the counter of events during the measurement period.
- */
-class TimeSeriesAverage implements TimeSeries {
-
- /** Value */
- private final TimeSeries value;
-
- /** Value */
- private final TimeSeries counter;
-
- public TimeSeriesAverage(TimeSeries value, TimeSeries counter) {
- this.value = value;
- this.counter = counter;
- }
-
- //----------------------------------------------------------< TimeSeries >
-
- public long[] getValuePerSecond() {
- long[] values = value.getValuePerSecond();
- long[] counts = counter.getValuePerSecond();
- return divide(values, counts);
- }
-
- public long[] getValuePerMinute() {
- long[] values = value.getValuePerMinute();
- long[] counts = counter.getValuePerMinute();
- return divide(values, counts);
- }
-
- public synchronized long[] getValuePerHour() {
- long[] values = value.getValuePerHour();
- long[] counts = counter.getValuePerHour();
- return divide(values, counts);
- }
-
- public synchronized long[] getValuePerWeek() {
- long[] values = value.getValuePerWeek();
- long[] counts = counter.getValuePerWeek();
- return divide(values, counts);
- }
-
- //-------------------------------------------------------------< private >
-
- /**
- * Per-entry division of two arrays.
- *
- * @param a array
- * @param b array
- * @return result of division
- */
- private long[] divide(long[] a, long[] b) {
- long[] c = new long[a.length];
- for (int i = 0; i < a.length; i++) {
- if (b[i] != 0) {
- c[i] = a[i] / b[i];
- } else {
- c[i] = 0;
- }
- }
- return c;
- }
-
-}
diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/TimeSeriesRecorder.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/TimeSeriesRecorder.java
deleted file mode 100755
index 533615c..0000000
--- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/stats/TimeSeriesRecorder.java
+++ /dev/null
@@ -1,161 +0,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.
- */
-package org.apache.jackrabbit.core.stats;
-
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.apache.jackrabbit.api.stats.TimeSeries;
-import org.apache.jackrabbit.api.stats.RepositoryStatistics.Type;
-
-/**
- * Recorder of a time series. An instance of this class records (and clears)
- * the state of a given {@link AtomicLong} counter once every second and
- * exposes the collected time series through the {@link TimeSeries}
- * interface.
- */
-class TimeSeriesRecorder implements TimeSeries {
-
- /** Value */
- private final AtomicLong counter = new AtomicLong();
-
- /** Whether to reset value each second */
- private final boolean resetValueEachSecond;
-
- /** Measured value per second over the last minute. */
- private final long[] valuePerSecond = new long[60];
-
- /** Measured value per minute over the last hour. */
- private final long[] valuePerMinute = new long[60];
-
- /** Measured value per hour over the last week. */
- private final long[] valuePerHour = new long[7 * 24];
-
- /** Measured value per week over the last three years. */
- private final long[] valuePerWeek = new long[3 * 52];
-
- /** Current second (index in {@link #valuePerSecond}) */
- private int seconds = 0;
-
- /** Current minute (index in {@link #valuePerMinute}) */
- private int minutes = 0;
-
- /** Current hour (index in {@link #valuePerHour}) */
- private int hours = 0;
-
- /** Current week (index in {@link #valuePerWeek}) */
- private int weeks = 0;
-
- public TimeSeriesRecorder(Type type) {
- this(type.isResetValueEachSecond());
- }
-
- public TimeSeriesRecorder(boolean resetValueEachSecond) {
- this.resetValueEachSecond = resetValueEachSecond;
- }
-
- /**
- * Returns the {@link AtomicLong} instance used to measure the value for
- * the time series.
- *
- * @return value
- */
- public AtomicLong getCounter() {
- return counter;
- }
-
- /**
- * Records the number of measured values over the past second and resets
- * the counter. This method should be scheduled to be called once per
- * second.
- */
- public synchronized void recordOneSecond() {
- if (resetValueEachSecond) {
- valuePerSecond[seconds++] = counter.getAndSet(0);
- } else {
- valuePerSecond[seconds++] = counter.get();
- }
- if (seconds == valuePerSecond.length) {
- seconds = 0;
- valuePerMinute[minutes++] = aggregate(valuePerSecond);
- }
- if (minutes == valuePerMinute.length) {
- minutes = 0;
- valuePerHour[hours++] = aggregate(valuePerMinute);
- }
- if (hours == valuePerHour.length) {
- hours = 0;
- valuePerWeek[weeks++] = aggregate(valuePerHour);
- }
- if (weeks == valuePerWeek.length) {
- weeks = 0;
- }
- }
-
- //----------------------------------------------------------< TimeSeries >
-
- public synchronized long[] getValuePerSecond() {
- return cyclicCopyFrom(valuePerSecond, seconds);
- }
-
- public synchronized long[] getValuePerMinute() {
- return cyclicCopyFrom(valuePerMinute, minutes);
- }
-
- public synchronized long[] getValuePerHour() {
- return cyclicCopyFrom(valuePerHour, hours);
- }
-
- public synchronized long[] getValuePerWeek() {
- return cyclicCopyFrom(valuePerWeek, weeks);
- }
-
- //-------------------------------------------------------------< private >
-
- /**
- * Returns the sum of all entries in the given array.
- *
- * @param array array to be summed
- * @return sum of entries
- */
- private long aggregate(long[] array) {
- long sum = 0;
- for (int i = 0; i < array.length; i++) {
-
- sum += array[i];
- }
- if (resetValueEachSecond) {
- return sum;
- }
- return sum / array.length;
- }
-
- /**
- * Returns a copy of the given cyclical array, with the element at
- * the given position as the first element of the returned array.
- *
- * @param array cyclical array
- * @param pos position of the first element
- * @return copy of the array
- */
- private static long[] cyclicCopyFrom(long[] array, int pos) {
- long[] reverse = new long[array.length];
- for (int i = 0; i < array.length; i++) {
- reverse[i] = array[(pos + i) % array.length];
- }
- return reverse;
- }
-}
diff --git a/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/PersistenceManagerTest.java b/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/PersistenceManagerTest.java
index 0cedb02..d314f48 100644
--- a/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/PersistenceManagerTest.java
+++ b/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/PersistenceManagerTest.java
@@ -18,8 +18,6 @@ package org.apache.jackrabbit.core.persistence;
import java.io.File;
import java.util.Arrays;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
import javax.jcr.PropertyType;
@@ -31,8 +29,6 @@ import org.apache.jackrabbit.core.RepositoryImpl;
import org.apache.jackrabbit.core.fs.mem.MemoryFileSystem;
import org.apache.jackrabbit.core.id.NodeId;
import org.apache.jackrabbit.core.id.PropertyId;
-import org.apache.jackrabbit.core.persistence.PMContext;
-import org.apache.jackrabbit.core.persistence.PersistenceManager;
import org.apache.jackrabbit.core.persistence.mem.InMemBundlePersistenceManager;
import org.apache.jackrabbit.core.persistence.mem.InMemPersistenceManager;
import org.apache.jackrabbit.core.persistence.obj.ObjectPersistenceManager;
@@ -43,7 +39,7 @@ import org.apache.jackrabbit.core.state.NoSuchItemStateException;
import org.apache.jackrabbit.core.state.NodeReferences;
import org.apache.jackrabbit.core.state.NodeState;
import org.apache.jackrabbit.core.state.PropertyState;
-import org.apache.jackrabbit.core.stats.RepositoryStatisticsImpl;
+import org.apache.jackrabbit.stats.RepositoryStatisticsImpl;
import org.apache.jackrabbit.core.util.db.ConnectionFactory;
import org.apache.jackrabbit.core.value.InternalValue;
import org.apache.jackrabbit.spi.Name;
diff --git a/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/stats/QueryStatCoreTest.java b/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/stats/QueryStatCoreTest.java
index 90bb3b3..5d14c15 100644
--- a/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/stats/QueryStatCoreTest.java
+++ b/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/stats/QueryStatCoreTest.java
@@ -20,6 +20,8 @@ import static javax.jcr.query.Query.JCR_SQL2;
import java.util.concurrent.atomic.AtomicLong;
+import org.apache.jackrabbit.stats.QueryStatCore;
+import org.apache.jackrabbit.stats.QueryStatImpl;
import org.apache.jackrabbit.test.AbstractJCRTest;
/**
@@ -74,4 +76,4 @@ public class QueryStatCoreTest extends AbstractJCRTest {
queryStat.setPopularQueriesQueueSize(newSize);
assertEquals(newSize, queryStat.getPopularQueries().length);
}
-}
\ No newline at end of file
+}
diff --git a/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/stats/RepositoryStatisticsImplTest.java b/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/stats/RepositoryStatisticsImplTest.java
deleted file mode 100644
index ee80918..0000000
--- a/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/stats/RepositoryStatisticsImplTest.java
+++ /dev/null
@@ -1,60 +0,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.
- */
-package org.apache.jackrabbit.core.stats;
-
-import java.util.Iterator;
-import java.util.Map.Entry;
-
-import junit.framework.TestCase;
-
-import org.apache.jackrabbit.api.stats.TimeSeries;
-
-public class RepositoryStatisticsImplTest extends TestCase {
-
- private static final int DEFAULT_NUMBER_OF_ELEMENTS = 17;
-
- public void testDefaultIterator() {
- RepositoryStatisticsImpl repositoryStatistics = new RepositoryStatisticsImpl();
-
- Iterator> iterator = repositoryStatistics.iterator();
- int count = 0;
- while (iterator.hasNext()) {
- count++;
- iterator.next();
- }
- assertEquals(DEFAULT_NUMBER_OF_ELEMENTS, count);
- }
-
- public void testIteratorWithSingleCustomType() {
- RepositoryStatisticsImpl repositoryStatistics = new RepositoryStatisticsImpl();
- String type = "customType";
- repositoryStatistics.getCounter(type, false);
-
- Iterator> iterator = repositoryStatistics.iterator();
- int count = 0;
- boolean customTypeExists = false;
- while (iterator.hasNext()) {
- count++;
- Entry entry = iterator.next();
- if (type.equals(entry.getKey())) {
- customTypeExists = true;
- }
- }
- assertEquals(DEFAULT_NUMBER_OF_ELEMENTS + 1, count);
- assertTrue(customTypeExists);
- }
-}
diff --git a/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/stats/TestAll.java b/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/stats/TestAll.java
index 5a4426e..426ee30 100644
--- a/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/stats/TestAll.java
+++ b/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/stats/TestAll.java
@@ -35,8 +35,6 @@ public class TestAll extends TestCase {
public static Test suite() {
TestSuite suite = new TestSuite("Stats tests");
- suite.addTestSuite(RepositoryStatisticsImplTest.class);
- suite.addTestSuite(TimeSeriesRecorderTest.class);
suite.addTestSuite(QueryStatCoreTest.class);
return suite;
diff --git a/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/stats/TimeSeriesRecorderTest.java b/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/stats/TimeSeriesRecorderTest.java
deleted file mode 100644
index 11cb4b6..0000000
--- a/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/stats/TimeSeriesRecorderTest.java
+++ /dev/null
@@ -1,123 +0,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.
- */
-package org.apache.jackrabbit.core.stats;
-
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.apache.jackrabbit.api.stats.RepositoryStatistics;
-
-import junit.framework.TestCase;
-
-public class TimeSeriesRecorderTest extends TestCase {
-
- public void testCounter() {
- TimeSeriesRecorder recorder = new TimeSeriesRecorder(
- RepositoryStatistics.Type.SESSION_READ_COUNTER);
- AtomicLong counter = recorder.getCounter();
-
- // initial values
- assertValues(recorder.getValuePerSecond());
- assertValues(recorder.getValuePerMinute());
- assertValues(recorder.getValuePerHour());
- assertValues(recorder.getValuePerWeek());
-
- // no changes in first second
- recorder.recordOneSecond();
- assertValues(recorder.getValuePerSecond());
- assertValues(recorder.getValuePerMinute());
- assertValues(recorder.getValuePerHour());
- assertValues(recorder.getValuePerWeek());
-
- // one increment in second
- counter.incrementAndGet();
- recorder.recordOneSecond();
- assertValues(recorder.getValuePerSecond(), 1);
- assertValues(recorder.getValuePerMinute());
- assertValues(recorder.getValuePerHour());
- assertValues(recorder.getValuePerWeek());
-
- // two increments in second
- counter.incrementAndGet();
- counter.incrementAndGet();
- recorder.recordOneSecond();
- assertValues(recorder.getValuePerSecond(), 2, 1);
- assertValues(recorder.getValuePerMinute());
- assertValues(recorder.getValuePerHour());
- assertValues(recorder.getValuePerWeek());
-
- // no changes in a second
- recorder.recordOneSecond();
- assertValues(recorder.getValuePerSecond(), 0, 2, 1);
- assertValues(recorder.getValuePerMinute());
- assertValues(recorder.getValuePerHour());
- assertValues(recorder.getValuePerWeek());
-
- // ten increments in a second
- counter.addAndGet(10);
- recorder.recordOneSecond();
- assertValues(recorder.getValuePerSecond(), 10, 0, 2, 1);
- assertValues(recorder.getValuePerMinute());
- assertValues(recorder.getValuePerHour());
- assertValues(recorder.getValuePerWeek());
-
- // one minute
- for (int i = 0; i < 60; i++) {
- recorder.recordOneSecond();
- }
- assertValues(recorder.getValuePerSecond());
- assertValues(recorder.getValuePerMinute(), 13);
- assertValues(recorder.getValuePerHour());
- assertValues(recorder.getValuePerWeek());
-
- // second minute
- for (int i = 0; i < 60; i++) {
- recorder.recordOneSecond();
- }
- assertValues(recorder.getValuePerSecond());
- assertValues(recorder.getValuePerMinute(), 0, 13);
- assertValues(recorder.getValuePerHour());
- assertValues(recorder.getValuePerWeek());
-
- // one hour
- for (int i = 0; i < 60 * 60; i++) {
- recorder.recordOneSecond();
- }
- assertValues(recorder.getValuePerSecond());
- assertValues(recorder.getValuePerMinute());
- assertValues(recorder.getValuePerHour(), 13);
- assertValues(recorder.getValuePerWeek());
-
- // one week
- for (int i = 0; i < 7 * 24 * 60 * 60; i++) {
- recorder.recordOneSecond();
- }
- assertValues(recorder.getValuePerSecond());
- assertValues(recorder.getValuePerMinute());
- assertValues(recorder.getValuePerHour());
- assertValues(recorder.getValuePerWeek(), 13);
- }
-
- private void assertValues(long[] values, long... expected) {
- for (int i = 0; i < expected.length; i++) {
- assertEquals(expected[i], values[values.length - i - 1]);
- }
- for (int i = expected.length; i < values.length; i++) {
- assertEquals(0, values[values.length - i - 1]);
- }
- }
-
-}
diff --git a/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/QueryStatCore.java b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/QueryStatCore.java
new file mode 100644
index 0000000..7d12120
--- /dev/null
+++ b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/QueryStatCore.java
@@ -0,0 +1,40 @@
+/*
+ * 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.jackrabbit.stats;
+
+import org.apache.jackrabbit.api.stats.QueryStat;
+
+/**
+ * Extends external facing {@link QueryStat} with some internal operations
+ *
+ */
+public interface QueryStatCore extends QueryStat {
+
+ /**
+ * Logs the call of each query ran on the repository.
+ *
+ * @param language
+ * the query language, see
+ * {@link org.apache.jackrabbit.spi.commons.name.NameConstants#JCR_LANGUAGE}
+ * @param statement
+ * the query
+ * @param durationMs
+ * time in ms
+ */
+ void logQuery(final String language, final String statement, long durationMs);
+
+}
diff --git a/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/QueryStatDtoComparator.java b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/QueryStatDtoComparator.java
new file mode 100644
index 0000000..7a14e93
--- /dev/null
+++ b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/QueryStatDtoComparator.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.
+ */
+package org.apache.jackrabbit.stats;
+
+import java.util.Comparator;
+
+import org.apache.jackrabbit.api.stats.QueryStatDto;
+
+/**
+ * QueryStatDto comparator by duration
+ *
+ */
+public class QueryStatDtoComparator implements Comparator {
+ public int compare(QueryStatDto o1, QueryStatDto o2) {
+ return new Long(o1.getDuration()).compareTo(o2.getDuration());
+ }
+}
diff --git a/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/QueryStatDtoImpl.java b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/QueryStatDtoImpl.java
new file mode 100644
index 0000000..06323aa
--- /dev/null
+++ b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/QueryStatDtoImpl.java
@@ -0,0 +1,145 @@
+/*
+ * 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.jackrabbit.stats;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.apache.jackrabbit.api.stats.QueryStatDto;
+
+/**
+ * Object that holds statistical info about a query.
+ *
+ */
+public class QueryStatDtoImpl implements QueryStatDto {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * lazy, computed at call time
+ */
+ private long position;
+
+ /**
+ * the time that the query was created
+ */
+ private final Date creationTime;
+
+ /**
+ * run duration in ms
+ */
+ private final long durationMs;
+
+ /**
+ * query language
+ */
+ private final String language;
+
+ /**
+ * query statement
+ */
+ private final String statement;
+
+ /**
+ * used in popular queries list
+ */
+ private int occurrenceCount = 1;
+
+ public QueryStatDtoImpl(final String language, final String statement,
+ long durationMs) {
+ this.durationMs = durationMs;
+ this.language = language;
+ this.statement = statement;
+
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(System.currentTimeMillis() - durationMs);
+ this.creationTime = c.getTime();
+ }
+
+ public long getDuration() {
+ return durationMs;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public String getStatement() {
+ return statement;
+ }
+
+ public String getCreationTime() {
+ return creationTime.toString();
+ }
+
+ public long getPosition() {
+ return position;
+ }
+
+ public void setPosition(long position) {
+ this.position = position;
+ }
+
+ @Override
+ public String toString() {
+ return "QueryStat [creationTime=" + creationTime + ", duration="
+ + durationMs + ", position " + position + ", language="
+ + language + ", statement=" + statement + "]";
+ }
+
+ public int getOccurrenceCount() {
+ return occurrenceCount;
+ }
+
+ public void setOccurrenceCount(int occurrenceCount) {
+ this.occurrenceCount = occurrenceCount;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((language == null) ? 0 : language.hashCode());
+ result = prime * result
+ + ((statement == null) ? 0 : statement.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ QueryStatDtoImpl other = (QueryStatDtoImpl) obj;
+ if (language == null) {
+ if (other.language != null)
+ return false;
+ } else if (!language.equals(other.language))
+ return false;
+ if (statement == null) {
+ if (other.statement != null)
+ return false;
+ } else if (!statement.equals(other.statement))
+ return false;
+ return true;
+ }
+
+}
diff --git a/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/QueryStatDtoOccurrenceComparator.java b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/QueryStatDtoOccurrenceComparator.java
new file mode 100644
index 0000000..dcaf5a6
--- /dev/null
+++ b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/QueryStatDtoOccurrenceComparator.java
@@ -0,0 +1,33 @@
+/*
+ * 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.jackrabbit.stats;
+
+import java.util.Comparator;
+
+/**
+ * QueryStatDto comparator by occurrence count
+ *
+ * used by the popular queries queue
+ *
+ */
+public class QueryStatDtoOccurrenceComparator implements
+ Comparator {
+ public int compare(QueryStatDtoImpl o1, QueryStatDtoImpl o2) {
+ return new Integer(o1.getOccurrenceCount()).compareTo(o2
+ .getOccurrenceCount());
+ }
+}
diff --git a/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/QueryStatImpl.java b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/QueryStatImpl.java
new file mode 100644
index 0000000..fb23500
--- /dev/null
+++ b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/QueryStatImpl.java
@@ -0,0 +1,186 @@
+/*
+ * 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.jackrabbit.stats;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.concurrent.PriorityBlockingQueue;
+
+import org.apache.jackrabbit.api.stats.QueryStatDto;
+
+/**
+ * Default {@link QueryStatCore} implementation
+ *
+ */
+public class QueryStatImpl implements QueryStatCore {
+
+ private final static Comparator comparator = new QueryStatDtoComparator();
+
+ private final BoundedPriorityBlockingQueue slowQueries = new BoundedPriorityBlockingQueue(
+ 15, comparator);
+
+ private final static Comparator comparatorOccurrence = new QueryStatDtoOccurrenceComparator();
+
+ /**
+ * the real queue size will be bigger than the desired number of popular
+ * queries by POPULAR_QUEUE_MULTIPLIER times
+ */
+ private static final int POPULAR_QUEUE_MULTIPLIER = 5;
+
+ private final BoundedPriorityBlockingQueue popularQueries = new BoundedPriorityBlockingQueue(
+ 15 * POPULAR_QUEUE_MULTIPLIER, comparatorOccurrence);
+
+ private static final class BoundedPriorityBlockingQueue extends
+ PriorityBlockingQueue {
+
+ private static final long serialVersionUID = 1L;
+ private int maxSize;
+
+ public BoundedPriorityBlockingQueue(int maxSize,
+ Comparator super E> comparator) {
+ super(maxSize + 1, comparator);
+ this.maxSize = maxSize;
+ }
+
+ @Override
+ public boolean offer(E e) {
+ boolean s = super.offer(e);
+ if (!s) {
+ return false;
+ }
+ if (size() > maxSize) {
+ poll();
+ }
+ return true;
+ }
+
+ public synchronized void setMaxSize(int maxSize) {
+ if (maxSize < this.maxSize) {
+ // shrink the queue
+ int delta = super.size() - maxSize;
+ for (int i = 0; i < delta; i++) {
+ E t = poll();
+ if (t == null) {
+ break;
+ }
+ }
+ }
+ this.maxSize = maxSize;
+ }
+
+ public int getMaxSize() {
+ return maxSize;
+ }
+ }
+
+ private boolean enabled = false;
+
+ public QueryStatImpl() {
+ }
+
+ public int getSlowQueriesQueueSize() {
+ return slowQueries.getMaxSize();
+ }
+
+ public void setSlowQueriesQueueSize(int size) {
+ slowQueries.setMaxSize(size);
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public synchronized void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public void logQuery(final String language, final String statement,
+ long durationMs) {
+ if (!enabled) {
+ return;
+ }
+ final QueryStatDtoImpl qs = new QueryStatDtoImpl(language, statement,
+ durationMs);
+ slowQueries.offer(qs);
+
+ synchronized (popularQueries) {
+ Iterator iterator = popularQueries.iterator();
+ while (iterator.hasNext()) {
+ QueryStatDtoImpl qsdi = iterator.next();
+ if (qsdi.equals(qs)) {
+ qs.setOccurrenceCount(qsdi.getOccurrenceCount() + 1);
+ iterator.remove();
+ break;
+ }
+ }
+ popularQueries.offer(qs);
+ }
+ }
+
+ public void clearSlowQueriesQueue() {
+ slowQueries.clear();
+ }
+
+ public QueryStatDto[] getSlowQueries() {
+ QueryStatDto[] top = slowQueries.toArray(new QueryStatDto[slowQueries
+ .size()]);
+ Arrays.sort(top, Collections.reverseOrder(comparator));
+ for (int i = 0; i < top.length; i++) {
+ top[i].setPosition(i + 1);
+ }
+ return top;
+ }
+
+ public QueryStatDto[] getPopularQueries() {
+ QueryStatDtoImpl[] top;
+ int size = 0;
+ int maxSize = 0;
+ synchronized (popularQueries) {
+ top = popularQueries.toArray(new QueryStatDtoImpl[popularQueries
+ .size()]);
+ size = popularQueries.size();
+ maxSize = popularQueries.getMaxSize();
+ }
+ Arrays.sort(top, Collections.reverseOrder(comparatorOccurrence));
+ int retSize = Math.min(size, maxSize / POPULAR_QUEUE_MULTIPLIER);
+ QueryStatDto[] retval = new QueryStatDto[retSize];
+ for (int i = 0; i < retSize; i++) {
+ retval[i] = top[i];
+ retval[i].setPosition(i + 1);
+ }
+ return retval;
+ }
+
+ public int getPopularQueriesQueueSize() {
+ return popularQueries.getMaxSize() / POPULAR_QUEUE_MULTIPLIER;
+ }
+
+ public void setPopularQueriesQueueSize(int size) {
+ popularQueries.setMaxSize(size * POPULAR_QUEUE_MULTIPLIER);
+ }
+
+ public void clearPopularQueriesQueue() {
+ popularQueries.clear();
+ }
+
+ public void reset() {
+ clearSlowQueriesQueue();
+ clearPopularQueriesQueue();
+ }
+}
diff --git a/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/RepositoryStatisticsImpl.java b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/RepositoryStatisticsImpl.java
new file mode 100644
index 0000000..974732d
--- /dev/null
+++ b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/RepositoryStatisticsImpl.java
@@ -0,0 +1,114 @@
+/*
+ * 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.jackrabbit.stats;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.jackrabbit.api.stats.RepositoryStatistics;
+import org.apache.jackrabbit.api.stats.TimeSeries;
+
+public class RepositoryStatisticsImpl implements
+ Iterable>, RepositoryStatistics {
+
+ private final Map recorders =
+ new HashMap();
+
+ private final Map avg =
+ new HashMap();
+
+ public RepositoryStatisticsImpl() {
+ getOrCreateRecorder(Type.SESSION_COUNT);
+ getOrCreateRecorder(Type.SESSION_LOGIN_COUNTER);
+
+ createAvg(Type.SESSION_READ_COUNTER, Type.SESSION_READ_DURATION,
+ Type.SESSION_READ_AVERAGE);
+ createAvg(Type.SESSION_WRITE_COUNTER, Type.SESSION_WRITE_DURATION,
+ Type.SESSION_WRITE_AVERAGE);
+ createAvg(Type.BUNDLE_CACHE_MISS_COUNTER,
+ Type.BUNDLE_CACHE_MISS_DURATION, Type.BUNDLE_CACHE_MISS_AVERAGE);
+ createAvg(Type.BUNDLE_WRITE_COUNTER, Type.BUNDLE_WRITE_DURATION,
+ Type.BUNDLE_WRITE_AVERAGE);
+ createAvg(Type.QUERY_COUNT, Type.QUERY_DURATION, Type.QUERY_AVERAGE);
+
+ }
+
+ private void createAvg(Type count, Type duration, Type avgTs) {
+ avg.put(avgTs.name(), new TimeSeriesAverage(getOrCreateRecorder(duration),
+ getOrCreateRecorder(count)));
+ }
+
+ public RepositoryStatisticsImpl(ScheduledExecutorService executor) {
+ this();
+ executor.scheduleAtFixedRate(new Runnable() {
+ public void run() {
+ recordOneSecond();
+ }
+ }, 1, 1, TimeUnit.SECONDS);
+ }
+
+ public synchronized Iterator> iterator() {
+ Map map = new TreeMap();
+ map.putAll(recorders);
+ map.putAll(avg);
+ return map.entrySet().iterator();
+ }
+
+ public AtomicLong getCounter(Type type) {
+ return getOrCreateRecorder(type).getCounter();
+ }
+
+ public AtomicLong getCounter(String type, boolean resetValueEachSecond) {
+ return getOrCreateRecorder(type, resetValueEachSecond).getCounter();
+ }
+
+ public TimeSeries getTimeSeries(Type type) {
+ return getTimeSeries(type.name(), type.isResetValueEachSecond());
+ }
+
+ public TimeSeries getTimeSeries(String type, boolean resetValueEachSecond) {
+ if (avg.containsKey(type)) {
+ return avg.get(type);
+ }
+ return getOrCreateRecorder(type, resetValueEachSecond);
+ }
+
+ private synchronized TimeSeriesRecorder getOrCreateRecorder(Type type) {
+ return getOrCreateRecorder(type.name(), type.isResetValueEachSecond());
+ }
+
+ private synchronized TimeSeriesRecorder getOrCreateRecorder(String type, boolean resetValueEachSecond) {
+ TimeSeriesRecorder recorder = recorders.get(type);
+ if (recorder == null) {
+ recorder = new TimeSeriesRecorder(resetValueEachSecond);
+ recorders.put(type, recorder);
+ }
+ return recorder;
+ }
+
+ private synchronized void recordOneSecond() {
+ for (TimeSeriesRecorder recorder : recorders.values()) {
+ recorder.recordOneSecond();
+ }
+ }
+}
diff --git a/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesAverage.java b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesAverage.java
new file mode 100644
index 0000000..c7bf107
--- /dev/null
+++ b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesAverage.java
@@ -0,0 +1,85 @@
+/*
+ * 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.jackrabbit.stats;
+
+import org.apache.jackrabbit.api.stats.TimeSeries;
+
+/**
+ * Time series of the average calculated by dividing a measured
+ * value by the counter of events during the measurement period.
+ */
+class TimeSeriesAverage implements TimeSeries {
+
+ /** Value */
+ private final TimeSeries value;
+
+ /** Value */
+ private final TimeSeries counter;
+
+ public TimeSeriesAverage(TimeSeries value, TimeSeries counter) {
+ this.value = value;
+ this.counter = counter;
+ }
+
+ //----------------------------------------------------------< TimeSeries >
+
+ public long[] getValuePerSecond() {
+ long[] values = value.getValuePerSecond();
+ long[] counts = counter.getValuePerSecond();
+ return divide(values, counts);
+ }
+
+ public long[] getValuePerMinute() {
+ long[] values = value.getValuePerMinute();
+ long[] counts = counter.getValuePerMinute();
+ return divide(values, counts);
+ }
+
+ public synchronized long[] getValuePerHour() {
+ long[] values = value.getValuePerHour();
+ long[] counts = counter.getValuePerHour();
+ return divide(values, counts);
+ }
+
+ public synchronized long[] getValuePerWeek() {
+ long[] values = value.getValuePerWeek();
+ long[] counts = counter.getValuePerWeek();
+ return divide(values, counts);
+ }
+
+ //-------------------------------------------------------------< private >
+
+ /**
+ * Per-entry division of two arrays.
+ *
+ * @param a array
+ * @param b array
+ * @return result of division
+ */
+ private long[] divide(long[] a, long[] b) {
+ long[] c = new long[a.length];
+ for (int i = 0; i < a.length; i++) {
+ if (b[i] != 0) {
+ c[i] = a[i] / b[i];
+ } else {
+ c[i] = 0;
+ }
+ }
+ return c;
+ }
+
+}
diff --git a/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesRecorder.java b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesRecorder.java
new file mode 100755
index 0000000..310c636
--- /dev/null
+++ b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesRecorder.java
@@ -0,0 +1,161 @@
+/*
+ * 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.jackrabbit.stats;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.jackrabbit.api.stats.TimeSeries;
+import org.apache.jackrabbit.api.stats.RepositoryStatistics.Type;
+
+/**
+ * Recorder of a time series. An instance of this class records (and clears)
+ * the state of a given {@link AtomicLong} counter once every second and
+ * exposes the collected time series through the {@link TimeSeries}
+ * interface.
+ */
+public class TimeSeriesRecorder implements TimeSeries {
+
+ /** Value */
+ private final AtomicLong counter = new AtomicLong();
+
+ /** Whether to reset value each second */
+ private final boolean resetValueEachSecond;
+
+ /** Measured value per second over the last minute. */
+ private final long[] valuePerSecond = new long[60];
+
+ /** Measured value per minute over the last hour. */
+ private final long[] valuePerMinute = new long[60];
+
+ /** Measured value per hour over the last week. */
+ private final long[] valuePerHour = new long[7 * 24];
+
+ /** Measured value per week over the last three years. */
+ private final long[] valuePerWeek = new long[3 * 52];
+
+ /** Current second (index in {@link #valuePerSecond}) */
+ private int seconds = 0;
+
+ /** Current minute (index in {@link #valuePerMinute}) */
+ private int minutes = 0;
+
+ /** Current hour (index in {@link #valuePerHour}) */
+ private int hours = 0;
+
+ /** Current week (index in {@link #valuePerWeek}) */
+ private int weeks = 0;
+
+ public TimeSeriesRecorder(Type type) {
+ this(type.isResetValueEachSecond());
+ }
+
+ public TimeSeriesRecorder(boolean resetValueEachSecond) {
+ this.resetValueEachSecond = resetValueEachSecond;
+ }
+
+ /**
+ * Returns the {@link AtomicLong} instance used to measure the value for
+ * the time series.
+ *
+ * @return value
+ */
+ public AtomicLong getCounter() {
+ return counter;
+ }
+
+ /**
+ * Records the number of measured values over the past second and resets
+ * the counter. This method should be scheduled to be called once per
+ * second.
+ */
+ public synchronized void recordOneSecond() {
+ if (resetValueEachSecond) {
+ valuePerSecond[seconds++] = counter.getAndSet(0);
+ } else {
+ valuePerSecond[seconds++] = counter.get();
+ }
+ if (seconds == valuePerSecond.length) {
+ seconds = 0;
+ valuePerMinute[minutes++] = aggregate(valuePerSecond);
+ }
+ if (minutes == valuePerMinute.length) {
+ minutes = 0;
+ valuePerHour[hours++] = aggregate(valuePerMinute);
+ }
+ if (hours == valuePerHour.length) {
+ hours = 0;
+ valuePerWeek[weeks++] = aggregate(valuePerHour);
+ }
+ if (weeks == valuePerWeek.length) {
+ weeks = 0;
+ }
+ }
+
+ //----------------------------------------------------------< TimeSeries >
+
+ public synchronized long[] getValuePerSecond() {
+ return cyclicCopyFrom(valuePerSecond, seconds);
+ }
+
+ public synchronized long[] getValuePerMinute() {
+ return cyclicCopyFrom(valuePerMinute, minutes);
+ }
+
+ public synchronized long[] getValuePerHour() {
+ return cyclicCopyFrom(valuePerHour, hours);
+ }
+
+ public synchronized long[] getValuePerWeek() {
+ return cyclicCopyFrom(valuePerWeek, weeks);
+ }
+
+ //-------------------------------------------------------------< private >
+
+ /**
+ * Returns the sum of all entries in the given array.
+ *
+ * @param array array to be summed
+ * @return sum of entries
+ */
+ private long aggregate(long[] array) {
+ long sum = 0;
+ for (int i = 0; i < array.length; i++) {
+
+ sum += array[i];
+ }
+ if (resetValueEachSecond) {
+ return sum;
+ }
+ return sum / array.length;
+ }
+
+ /**
+ * Returns a copy of the given cyclical array, with the element at
+ * the given position as the first element of the returned array.
+ *
+ * @param array cyclical array
+ * @param pos position of the first element
+ * @return copy of the array
+ */
+ private static long[] cyclicCopyFrom(long[] array, int pos) {
+ long[] reverse = new long[array.length];
+ for (int i = 0; i < array.length; i++) {
+ reverse[i] = array[(pos + i) % array.length];
+ }
+ return reverse;
+ }
+}
diff --git a/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/jmx/QueryStatManager.java b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/jmx/QueryStatManager.java
new file mode 100644
index 0000000..d1b7bf3
--- /dev/null
+++ b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/jmx/QueryStatManager.java
@@ -0,0 +1,139 @@
+/*
+ * 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.jackrabbit.stats.jmx;
+
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+
+import org.apache.jackrabbit.api.jmx.QueryStatManagerMBean;
+import org.apache.jackrabbit.api.stats.QueryStat;
+import org.apache.jackrabbit.api.stats.QueryStatDto;
+
+/**
+ * The QueryStatManagerMBean default implementation
+ *
+ */
+public class QueryStatManager implements QueryStatManagerMBean {
+
+ private final QueryStat queryStat;
+
+ public QueryStatManager(final QueryStat queryStat) {
+ this.queryStat = queryStat;
+ }
+
+ public boolean isEnabled() {
+ return this.queryStat.isEnabled();
+ }
+
+ public void enable() {
+ this.queryStat.setEnabled(true);
+ }
+
+ public void disable() {
+ this.queryStat.setEnabled(false);
+ }
+
+ public void reset() {
+ this.queryStat.reset();
+ }
+
+ public int getSlowQueriesQueueSize() {
+ return queryStat.getSlowQueriesQueueSize();
+ }
+
+ public void setSlowQueriesQueueSize(int size) {
+ this.queryStat.setSlowQueriesQueueSize(size);
+ }
+
+ public void clearSlowQueriesQueue() {
+ this.queryStat.clearSlowQueriesQueue();
+ }
+
+ public int getPopularQueriesQueueSize() {
+ return queryStat.getPopularQueriesQueueSize();
+ }
+
+ public void setPopularQueriesQueueSize(int size) {
+ queryStat.setPopularQueriesQueueSize(size);
+ }
+
+ public void clearPopularQueriesQueue() {
+ queryStat.clearPopularQueriesQueue();
+ }
+
+ public TabularData getSlowQueries() {
+ return asTabularData(queryStat.getSlowQueries());
+ }
+
+ public TabularData getPopularQueries() {
+ return asTabularData(queryStat.getPopularQueries());
+ }
+
+ private TabularData asTabularData(QueryStatDto[] data) {
+ TabularDataSupport tds = null;
+ try {
+ CompositeType ct = QueryStatCompositeTypeFactory.getCompositeType();
+
+ TabularType tt = new TabularType(QueryStatDto.class.getName(),
+ "Query History", ct, QueryStatCompositeTypeFactory.index);
+ tds = new TabularDataSupport(tt);
+
+ for (QueryStatDto q : data) {
+ tds.put(new CompositeDataSupport(ct,
+ QueryStatCompositeTypeFactory.names,
+ QueryStatCompositeTypeFactory.getValues(q)));
+ }
+ return tds;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ private static class QueryStatCompositeTypeFactory {
+
+ private final static String[] index = { "position" };
+
+ private final static String[] names = { "position", "duration",
+ "occurrenceCount", "language", "statement", "creationTime" };
+
+ private final static String[] descriptions = { "position", "duration",
+ "occurrenceCount", "language", "statement", "creationTime" };
+
+ private final static OpenType[] types = { SimpleType.LONG,
+ SimpleType.LONG, SimpleType.INTEGER, SimpleType.STRING,
+ SimpleType.STRING, SimpleType.STRING };
+
+ public static CompositeType getCompositeType() throws OpenDataException {
+ return new CompositeType(QueryStat.class.getName(),
+ QueryStat.class.getName(), names, descriptions, types);
+ }
+
+ public static Object[] getValues(QueryStatDto q) {
+ return new Object[] { q.getPosition(), q.getDuration(),
+ q.getOccurrenceCount(), q.getLanguage(), q.getStatement(),
+ q.getCreationTime() };
+ }
+ }
+
+}
diff --git a/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/RepositoryStatisticsImplTest.java b/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/RepositoryStatisticsImplTest.java
new file mode 100644
index 0000000..616396f
--- /dev/null
+++ b/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/RepositoryStatisticsImplTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.jackrabbit.stats;
+
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+import junit.framework.TestCase;
+import org.apache.jackrabbit.api.stats.TimeSeries;
+
+public class RepositoryStatisticsImplTest extends TestCase {
+
+ private static final int DEFAULT_NUMBER_OF_ELEMENTS = 17;
+
+ public void testDefaultIterator() {
+ RepositoryStatisticsImpl repositoryStatistics = new RepositoryStatisticsImpl();
+
+ Iterator> iterator = repositoryStatistics.iterator();
+ int count = 0;
+ while (iterator.hasNext()) {
+ count++;
+ iterator.next();
+ }
+ assertEquals(DEFAULT_NUMBER_OF_ELEMENTS, count);
+ }
+
+ public void testIteratorWithSingleCustomType() {
+ RepositoryStatisticsImpl repositoryStatistics = new RepositoryStatisticsImpl();
+ String type = "customType";
+ repositoryStatistics.getCounter(type, false);
+
+ Iterator> iterator = repositoryStatistics.iterator();
+ int count = 0;
+ boolean customTypeExists = false;
+ while (iterator.hasNext()) {
+ count++;
+ Entry entry = iterator.next();
+ if (type.equals(entry.getKey())) {
+ customTypeExists = true;
+ }
+ }
+ assertEquals(DEFAULT_NUMBER_OF_ELEMENTS + 1, count);
+ assertTrue(customTypeExists);
+ }
+}
diff --git a/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesRecorderTest.java b/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesRecorderTest.java
new file mode 100644
index 0000000..5bf4fb5
--- /dev/null
+++ b/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesRecorderTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.jackrabbit.stats;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import junit.framework.TestCase;
+import org.apache.jackrabbit.api.stats.RepositoryStatistics;
+
+public class TimeSeriesRecorderTest extends TestCase {
+
+ public void testCounter() {
+ TimeSeriesRecorder recorder = new TimeSeriesRecorder(
+ RepositoryStatistics.Type.SESSION_READ_COUNTER);
+ AtomicLong counter = recorder.getCounter();
+
+ // initial values
+ assertValues(recorder.getValuePerSecond());
+ assertValues(recorder.getValuePerMinute());
+ assertValues(recorder.getValuePerHour());
+ assertValues(recorder.getValuePerWeek());
+
+ // no changes in first second
+ recorder.recordOneSecond();
+ assertValues(recorder.getValuePerSecond());
+ assertValues(recorder.getValuePerMinute());
+ assertValues(recorder.getValuePerHour());
+ assertValues(recorder.getValuePerWeek());
+
+ // one increment in second
+ counter.incrementAndGet();
+ recorder.recordOneSecond();
+ assertValues(recorder.getValuePerSecond(), 1);
+ assertValues(recorder.getValuePerMinute());
+ assertValues(recorder.getValuePerHour());
+ assertValues(recorder.getValuePerWeek());
+
+ // two increments in second
+ counter.incrementAndGet();
+ counter.incrementAndGet();
+ recorder.recordOneSecond();
+ assertValues(recorder.getValuePerSecond(), 2, 1);
+ assertValues(recorder.getValuePerMinute());
+ assertValues(recorder.getValuePerHour());
+ assertValues(recorder.getValuePerWeek());
+
+ // no changes in a second
+ recorder.recordOneSecond();
+ assertValues(recorder.getValuePerSecond(), 0, 2, 1);
+ assertValues(recorder.getValuePerMinute());
+ assertValues(recorder.getValuePerHour());
+ assertValues(recorder.getValuePerWeek());
+
+ // ten increments in a second
+ counter.addAndGet(10);
+ recorder.recordOneSecond();
+ assertValues(recorder.getValuePerSecond(), 10, 0, 2, 1);
+ assertValues(recorder.getValuePerMinute());
+ assertValues(recorder.getValuePerHour());
+ assertValues(recorder.getValuePerWeek());
+
+ // one minute
+ for (int i = 0; i < 60; i++) {
+ recorder.recordOneSecond();
+ }
+ assertValues(recorder.getValuePerSecond());
+ assertValues(recorder.getValuePerMinute(), 13);
+ assertValues(recorder.getValuePerHour());
+ assertValues(recorder.getValuePerWeek());
+
+ // second minute
+ for (int i = 0; i < 60; i++) {
+ recorder.recordOneSecond();
+ }
+ assertValues(recorder.getValuePerSecond());
+ assertValues(recorder.getValuePerMinute(), 0, 13);
+ assertValues(recorder.getValuePerHour());
+ assertValues(recorder.getValuePerWeek());
+
+ // one hour
+ for (int i = 0; i < 60 * 60; i++) {
+ recorder.recordOneSecond();
+ }
+ assertValues(recorder.getValuePerSecond());
+ assertValues(recorder.getValuePerMinute());
+ assertValues(recorder.getValuePerHour(), 13);
+ assertValues(recorder.getValuePerWeek());
+
+ // one week
+ for (int i = 0; i < 7 * 24 * 60 * 60; i++) {
+ recorder.recordOneSecond();
+ }
+ assertValues(recorder.getValuePerSecond());
+ assertValues(recorder.getValuePerMinute());
+ assertValues(recorder.getValuePerHour());
+ assertValues(recorder.getValuePerWeek(), 13);
+ }
+
+ private void assertValues(long[] values, long... expected) {
+ for (int i = 0; i < expected.length; i++) {
+ assertEquals(expected[i], values[values.length - i - 1]);
+ }
+ for (int i = expected.length; i < values.length; i++) {
+ assertEquals(0, values[values.length - i - 1]);
+ }
+ }
+
+}