/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to You under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.apache.sling.engine.impl; import java.util.concurrent.atomic.AtomicReference; import javax.management.NotCompliantMBeanException; import javax.management.StandardMBean; import org.apache.sling.engine.impl.request.RequestData; import org.apache.sling.engine.jmx.RequestProcessorMBean; /** * This is the implementation of the management interface for the * RequestProcessor. */ class RequestProcessorMBeanImpl extends StandardMBean implements RequestProcessorMBean { private final AtomicReference<Data> dataRef = new AtomicReference<Data>(new Data()); RequestProcessorMBeanImpl() throws NotCompliantMBeanException { super(RequestProcessorMBean.class); } void addRequestData(final RequestData data) { // do a non-blocking busy loop and atomically set the new data // the advantage of this algorithm is that there is no blocking // involved // // there might be some memory churn under high contention, but that // remains to be seen for ( ;; ) { Data oldVal = dataRef.get(); Data newVal = new Data(oldVal, data); boolean success = dataRef.compareAndSet(oldVal, newVal); if ( success ) { break; } } } public void resetStatistics() { dataRef.set(new Data()); } public long getRequestsCount() { return dataRef.get().n; } public long getMinRequestDurationMsec() { return dataRef.get().durationMsecMin; } public long getMaxRequestDurationMsec() { return dataRef.get().durationMsecMax; } public double getStandardDeviationDurationMsec() { return dataRef.get().standardDeviationDurationMsec; } public double getMeanRequestDurationMsec() { return dataRef.get().meanRequestDurationMsec; } public int getMaxPeakRecursionDepth() { return dataRef.get().peakRecursionDepthMax; } public int getMinPeakRecursionDepth() { return dataRef.get().peakRecursionDepthMin; } public double getMeanPeakRecursionDepth() { return dataRef.get().meanPeakRecursionDepth; } public double getStandardDeviationPeakRecursionDepth() { return dataRef.get().standardDeviationPeakRecursionDepth; } public int getMaxServletCallCount() { return dataRef.get().servletCallCountMax; } public int getMinServletCallCount() { return dataRef.get().servletCallCountMin; } public double getMeanServletCallCount() { return dataRef.get().meanServletCallCount; } public double getStandardDeviationServletCallCount() { return dataRef.get().standardDeviationServletCallCount; } /** * Helper class to atomically hold raw data and compute statistics */ private static class Data { // number of requests private final long n; // shortest request private final long durationMsecMin; // longest request private final long durationMsecMax; // sum of request durations private final double durationMsecSumX; // sum of squared request durations private final double durationMsecSumX2; private final int servletCallCountMin; private final int servletCallCountMax; private final double servletCallCountSumX; private final double servletCallCountSumX2; private final int peakRecursionDepthMin; private final int peakRecursionDepthMax; private final double peakRecursionDepthSumX; private final double peakRecursionDepthSumX2; private final double standardDeviationDurationMsec; private final double meanRequestDurationMsec; private final double meanPeakRecursionDepth; private final double standardDeviationPeakRecursionDepth; private final double meanServletCallCount; private final double standardDeviationServletCallCount; // computed fields Data() { n = 0; durationMsecMin = Long.MAX_VALUE; durationMsecMax = 0; durationMsecSumX = 0; durationMsecSumX2 = 0; servletCallCountMin = Integer.MAX_VALUE; servletCallCountMax = 0; servletCallCountSumX = 0; servletCallCountSumX2 = 0; peakRecursionDepthMin = Integer.MAX_VALUE; peakRecursionDepthMax = 0; peakRecursionDepthSumX = 0; peakRecursionDepthSumX2 = 0; standardDeviationDurationMsec = computeStandardDeviationDurationMsec(); meanRequestDurationMsec = computeMeanRequestDurationMsec(); meanPeakRecursionDepth = computeMeanPeakRecursionDepth(); standardDeviationPeakRecursionDepth = computeStandardDeviationPeakRecursionDepth(); meanServletCallCount = computeMeanServletCallCount(); standardDeviationServletCallCount = computeStandardDeviationServletCallCount(); } Data(Data other, RequestData data) { if ( other == null || data == null ) { throw new IllegalArgumentException("Neither 'other' nor 'data' may be null"); } final long duration = data.getElapsedTimeMsec(); final int servletCallCount = data.getServletCallCount(); final int peakRecursionDepth = data.getPeakRecusionDepth(); n = other.n + 1; durationMsecMin = Math.min(duration, other.durationMsecMin); durationMsecMax = Math.max(duration, other.durationMsecMax); durationMsecSumX = other.durationMsecSumX + duration; durationMsecSumX2 = other.durationMsecSumX2 + (duration * duration); servletCallCountMin = Math.min(servletCallCount, other.servletCallCountMin); servletCallCountMax = Math.max(servletCallCount, other.servletCallCountMax); servletCallCountSumX = other.servletCallCountSumX + servletCallCount; servletCallCountSumX2 = other.servletCallCountSumX2 + (servletCallCount * servletCallCount); peakRecursionDepthMin = Math.min(peakRecursionDepth , other.peakRecursionDepthMin); peakRecursionDepthMax = Math.max(peakRecursionDepth , other.peakRecursionDepthMax); peakRecursionDepthSumX = other.peakRecursionDepthSumX + peakRecursionDepth; peakRecursionDepthSumX2 = other.peakRecursionDepthSumX2 + (peakRecursionDepth * peakRecursionDepth); standardDeviationDurationMsec = computeStandardDeviationDurationMsec(); meanRequestDurationMsec = computeMeanRequestDurationMsec(); meanPeakRecursionDepth = computeMeanPeakRecursionDepth(); standardDeviationPeakRecursionDepth = computeStandardDeviationPeakRecursionDepth(); meanServletCallCount = computeMeanServletCallCount(); standardDeviationServletCallCount = computeStandardDeviationServletCallCount(); } private double computeStandardDeviationDurationMsec() { if (this.n > 1) { // algorithm taken from // http://de.wikipedia.org/wiki/Standardabweichung section // "Berechnung fuer auflaufende Messwerte" return Math.sqrt((this.durationMsecSumX2 - this.durationMsecSumX * this.durationMsecSumX / this.n) / (this.n - 1)); } // single data point has no deviation return 0; } private double computeMeanRequestDurationMsec() { if (this.n > 0) { return this.durationMsecSumX / this.n; } else { return 0; } } private double computeMeanPeakRecursionDepth() { if (this.n > 0) { return this.peakRecursionDepthSumX / this.n; } else { return 0; } } private double computeStandardDeviationPeakRecursionDepth() { if (this.n > 1) { return Math.sqrt((this.peakRecursionDepthSumX2 - this.peakRecursionDepthSumX * this.peakRecursionDepthSumX / this.n) / (this.n - 1)); } // single data point has no deviation return 0; } private double computeMeanServletCallCount() { if (this.n > 0) { return this.servletCallCountSumX / this.n; } else { return 0; } } private double computeStandardDeviationServletCallCount() { if (this.n > 1) { return Math.sqrt((this.servletCallCountSumX2 - this.servletCallCountSumX * this.servletCallCountSumX / this.n) / (this.n - 1)); } // single data point has no deviation return 0; } } }