You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by md...@apache.org on 2015/03/12 13:49:56 UTC
svn commit: r1666170 - in /jackrabbit/trunk:
jackrabbit-api/src/main/java/org/apache/jackrabbit/api/jmx/
jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/observation/
jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/
Author: mduerig
Date: Thu Mar 12 12:49:56 2015
New Revision: 1666170
URL: http://svn.apache.org/r1666170
Log:
JCR-3859: Extend EventListenerMBean to report time series for observation processing
Initial implementation of the additional EventListenerMBean methods
Added:
jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java
jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesStatsUtil.java
Modified:
jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/jmx/EventListenerMBean.java
jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/observation/ListenerTracker.java
Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/jmx/EventListenerMBean.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/jmx/EventListenerMBean.java?rev=1666170&r1=1666169&r2=1666170&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/jmx/EventListenerMBean.java (original)
+++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/jmx/EventListenerMBean.java Thu Mar 12 12:49:56 2015
@@ -16,6 +16,8 @@
*/
package org.apache.jackrabbit.api.jmx;
+import javax.management.openmbean.CompositeData;
+
/**
* MBean interface for exposing information about a registered observation
* listener.
@@ -81,4 +83,28 @@ public interface EventListenerMBean {
/** Is date information accessed from an external event? */
boolean isDateAccessedFromExternalEvent();
+ /**
+ * {@link org.apache.jackrabbit.api.stats.TimeSeries time series} of the number of
+ * items related to generating observation events that are currently queued by the
+ * system. The exact nature of these items is implementation specific and might not
+ * be in a one to one relation with the number of pending JCR events.
+ * @return time series of the queue length
+ */
+ CompositeData getQueueLength();
+
+ /**
+ * @return time series of the number of JCR events
+ */
+ CompositeData getEventCount();
+
+ /**
+ * @return time series of the time it took an event listener to process JCR events.
+ */
+ CompositeData getEventConsumerTime();
+
+ /**
+ * @return time series of the time it took the system to produce JCR events.
+ */
+ CompositeData getEventProducerTime();
+
}
Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/observation/ListenerTracker.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/observation/ListenerTracker.java?rev=1666170&r1=1666169&r2=1666170&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/observation/ListenerTracker.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/observation/ListenerTracker.java Thu Mar 12 12:49:56 2015
@@ -20,6 +20,7 @@ package org.apache.jackrabbit.commons.ob
import static java.lang.System.currentTimeMillis;
import static java.lang.System.nanoTime;
+import static org.apache.jackrabbit.stats.TimeSeriesStatsUtil.asCompositeData;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -30,10 +31,13 @@ import java.util.concurrent.atomic.Atomi
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
+import javax.management.openmbean.CompositeData;
import org.apache.jackrabbit.api.jmx.EventListenerMBean;
import org.apache.jackrabbit.api.observation.JackrabbitEvent;
import org.apache.jackrabbit.commons.iterator.EventIteratorAdapter;
+import org.apache.jackrabbit.stats.TimeSeriesMax;
+import org.apache.jackrabbit.stats.TimeSeriesRecorder;
/**
* Tracks event deliveries to an event listener and the way the listener
@@ -67,6 +71,14 @@ public class ListenerTracker {
private final AtomicLong eventDeliveryTime = new AtomicLong();
+ private final TimeSeriesMax queueLength = new TimeSeriesMax();
+
+ private final TimeSeriesRecorder eventCount = new TimeSeriesRecorder(true);
+
+ private final TimeSeriesRecorder eventConsumerTime = new TimeSeriesRecorder(true);
+
+ private final TimeSeriesRecorder eventProducerTime = new TimeSeriesRecorder(true);
+
final AtomicBoolean userInfoAccessedWithoutExternalsCheck =
new AtomicBoolean();
@@ -80,11 +92,9 @@ public class ListenerTracker {
new AtomicBoolean();
public ListenerTracker(
- EventListener listener,
- int eventTypes, String absPath, boolean isDeep,
- String[] uuid, String[] nodeTypeName, boolean noLocal) {
+ EventListener listener, int eventTypes, String absPath, boolean isDeep, String[] uuid,
+ String[] nodeTypeName, boolean noLocal) {
this.listener = listener;
-
this.eventTypes = eventTypes;
this.absPath = absPath;
this.isDeep = isDeep;
@@ -123,18 +133,46 @@ public class ListenerTracker {
// do nothing
}
+ /**
+ * Applications should call this to report the current queue length.
+ * @param length
+ */
+ public void recordQueueLength(long length) {
+ queueLength.recordValue(length);
+ }
+
+ /**
+ * 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 void recordOneSecond() {
+ queueLength.recordOneSecond();
+ eventCount.recordOneSecond();
+ eventConsumerTime.recordOneSecond();
+ eventProducerTime.recordOneSecond();
+ }
+
public EventListener getTrackedListener() {
return new EventListener() {
@Override
public void onEvent(EventIterator events) {
eventDeliveries.incrementAndGet();
- long start = nanoTime();
+ final long start = nanoTime();
try {
beforeEventDelivery();
listener.onEvent(new EventIteratorAdapter(events) {
+ long t0 = start;
+
+ private void recordTime(TimeSeriesRecorder recorder) {
+ recorder.getCounter().addAndGet(-(t0 - (t0 = nanoTime())));
+ }
+
@Override
public Object next() {
+ recordTime(eventConsumerTime);
eventsDelivered.incrementAndGet();
+ eventCount.getCounter().incrementAndGet();
Object object = super.next();
if (object instanceof JackrabbitEvent) {
object = new JackrabbitEventTracker(
@@ -144,8 +182,18 @@ public class ListenerTracker {
object = new EventTracker(
ListenerTracker.this, (Event) object);
}
+ recordTime(eventProducerTime);
return object;
}
+
+ @Override
+ public boolean hasNext() {
+ recordTime(eventConsumerTime);
+ boolean result = super.hasNext();
+ t0 = nanoTime();
+ recordTime(eventProducerTime);
+ return result;
+ }
});
} finally {
afterEventDelivery();
@@ -247,6 +295,22 @@ public class ListenerTracker {
public synchronized boolean isDateAccessedFromExternalEvent() {
return dateAccessedFromExternalEvent.get();
}
+ @Override
+ public CompositeData getQueueLength() {
+ return asCompositeData(queueLength, "queueLength");
+ }
+ @Override
+ public CompositeData getEventCount() {
+ return asCompositeData(eventCount, "eventCount");
+ }
+ @Override
+ public CompositeData getEventConsumerTime() {
+ return asCompositeData(eventConsumerTime, "eventConsumerTime");
+ }
+ @Override
+ public CompositeData getEventProducerTime() {
+ return asCompositeData(eventProducerTime, "eventProducerTime");
+ }
};
}
Added: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java?rev=1666170&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java Thu Mar 12 12:49:56 2015
@@ -0,0 +1,142 @@
+/*
+ * 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 maximum value recorded in a period
+ */
+public class TimeSeriesMax implements TimeSeries {
+ private final MaxValue max = new MaxValue(0);
+ private final long[] perSecond = new long[60];
+ private final long[] perMinute = new long[60];
+ private final long[] perHour = new long[7 * 24];
+ private final long[] perWeek = new long[3 * 52];
+
+ /** Current second (index in {@link #perSecond}) */
+ private int seconds;
+
+ /** Current minute (index in {@link #perMinute}) */
+ private int minutes;
+
+ /** Current hour (index in {@link #perHour}) */
+ private int hours;
+
+ /** Current week (index in {@link #perWeek}) */
+ private int weeks;
+
+ public void recordValue(long value) {
+ max.setIfMaximal(value);
+ }
+
+ /**
+ * 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() {
+ perSecond[seconds++] = max.getAndSetValue(0);
+ if (seconds == perSecond.length) {
+ seconds = 0;
+ perMinute[minutes++] = max(perSecond);
+ }
+ if (minutes == perMinute.length) {
+ minutes = 0;
+ perHour[hours++] = max(perMinute);
+ }
+ if (hours == perHour.length) {
+ hours = 0;
+ perWeek[weeks++] = max(perHour);
+ }
+ if (weeks == perWeek.length) {
+ weeks = 0;
+ }
+ }
+
+ @Override
+ public synchronized long[] getValuePerSecond() {
+ return cyclicCopyFrom(perSecond, seconds);
+ }
+
+ @Override
+ public synchronized long[] getValuePerMinute() {
+ return cyclicCopyFrom(perMinute, minutes);
+ }
+
+ @Override
+ public synchronized long[] getValuePerHour() {
+ return cyclicCopyFrom(perHour, hours);
+ }
+
+ @Override
+ public synchronized long[] getValuePerWeek() {
+ return cyclicCopyFrom(perWeek, weeks);
+ }
+
+ /**
+ * Returns the maximum of all entries in the given array.
+ */
+ private static long max(long[] array) {
+ long max = Long.MIN_VALUE;
+ for (long v : array) {
+ if (v > max) {
+ max = v;
+ }
+ }
+ return max;
+ }
+
+ /**
+ * 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;
+ }
+
+ private static class MaxValue {
+ private long value;
+
+ public MaxValue(long value) {
+ this.value = value;
+ }
+
+ public synchronized long getAndSetValue(long value) {
+ long v = this.value;
+ this.value = value;
+ return v;
+ }
+
+ public synchronized void setIfMaximal(long value) {
+ if (value > this.value) {
+ this.value = value;
+ }
+ }
+ }
+}
Added: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesStatsUtil.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesStatsUtil.java?rev=1666170&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesStatsUtil.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesStatsUtil.java Thu Mar 12 12:49:56 2015
@@ -0,0 +1,56 @@
+/*
+ * 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 javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeData;
+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 org.apache.jackrabbit.api.stats.TimeSeries;
+
+/**
+ * Utility class for retrieving {@link javax.management.openmbean.CompositeData} for
+ * {@link org.apache.jackrabbit.api.stats.TimeSeries}.
+ */
+public final class TimeSeriesStatsUtil {
+ public static final String[] ITEM_NAMES = new String[] {"per second", "per minute", "per hour", "per week"};
+
+ private TimeSeriesStatsUtil() {
+ }
+
+ public static CompositeData asCompositeData(TimeSeries timeSeries, String name) {
+ try {
+ long[][] values = new long[][] {timeSeries.getValuePerSecond(), timeSeries.getValuePerMinute(),
+ timeSeries.getValuePerHour(), timeSeries.getValuePerWeek()};
+ return new CompositeDataSupport(getCompositeType(name), ITEM_NAMES, values);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Error creating CompositeData instance from TimeSeries", e);
+ }
+ }
+
+ private static CompositeType getCompositeType(String name) throws OpenDataException {
+ ArrayType<int[]> longArrayType = new ArrayType<int[]>(SimpleType.LONG, true);
+ OpenType<?>[] itemTypes = new OpenType[] {longArrayType, longArrayType, longArrayType, longArrayType};
+ return new CompositeType(name, name + " time series", ITEM_NAMES, ITEM_NAMES, itemTypes);
+ }
+}