/* * Copyright (c) 2011,2013 Big Switch Networks, Inc. * * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/legal/epl-v10.html * * 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. * * This file incorporates work covered by the following copyright and * permission notice: * * Originally created by David Erickson, Stanford University * * Licensed 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.sdnplatform.counter; import java.util.Date; import org.sdnplatform.counter.ICounter.DateSpan; /** * Implements a circular buffer to store the last x time-based counter values. This is pretty crumby * implementation, basically wrapping everything with synchronized blocks, in order to ensure that threads * which will be updating the series don't result in a thread which is reading the series getting stuck with * a start date which does not correspond to the count values in getSeries. * * This could probably use a re-think... * * @author kyle * */ public class CountBuffer { protected long[] counterValues; protected Date startDate; protected DateSpan dateSpan; protected int currentIndex; protected int seriesLength; public CountBuffer(Date startDate, DateSpan dateSpan, int seriesLength) { this.seriesLength = seriesLength; this.counterValues = new long[seriesLength]; this.dateSpan = dateSpan; this.startDate = startDate; this.currentIndex = 0; } /** * Increment the count associated with Date d, forgetting some of the older count values if necessary to ensure * that the total span of time covered by this series corresponds to DateSpan * seriesLength (circular buffer). * * Note - fails silently if the Date falls prior to the start of the tracked count values. * * Note - this should be a reasonably fast method, though it will have to block if there is another thread reading the * series at the same time. * * @param d * @param delta */ public synchronized void increment(Date d, long delta) { long dsMillis = CountSeries.dateSpanToMilliseconds(this.dateSpan); Date endDate = new Date(startDate.getTime() + seriesLength * dsMillis - 1); if(d.getTime() < startDate.getTime()) { return; //silently fail rather than insert a count at a time older than the history buffer we're keeping } else if (d.getTime() >= startDate.getTime() && d.getTime() <= endDate.getTime()) { int index = (int) (( d.getTime() - startDate.getTime() ) / dsMillis); // java rounds down on long/long int modIndex = (index + currentIndex) % seriesLength; long currentValue = counterValues[modIndex]; counterValues[modIndex] = currentValue + delta; } else if (d.getTime() > endDate.getTime()) { //Initialize new buckets int newBuckets = (int)((d.getTime() - endDate.getTime()) / dsMillis) + 1; // java rounds down on long/long for(int i = 0; i < newBuckets; i++) { int modIndex = (i + currentIndex) % seriesLength; counterValues[modIndex] = 0; } //Update internal vars this.startDate = new Date(startDate.getTime() + dsMillis * newBuckets); this.currentIndex = (currentIndex + newBuckets) % this.seriesLength; //Call again (date should be in the range this time) this.increment(d, delta); } } /** * Relatively slow method, expected to be called primarily from UI rather than from in-packet-path. * * @return the count values associated with each time interval starting with startDate and demarc'ed by dateSpan */ public long[] getSeries() { //synchronized here should lock on 'this', implying that it shares the lock with increment long[] ret = new long[this.seriesLength]; for(int i = 0; i < this.seriesLength; i++) { int modIndex = (currentIndex + i) % this.seriesLength; ret[i] = this.counterValues[modIndex]; } return ret; } /** * Returns an immutable count series that represents a snapshot of this * series at a specific moment in time. * @return */ public synchronized CountSeries snapshot() { long[] cvs = new long[this.seriesLength]; for(int i = 0; i < this.seriesLength; i++) { int modIndex = (this.currentIndex + i) % this.seriesLength; cvs[i] = this.counterValues[modIndex]; } return new CountSeries(this.startDate, this.dateSpan, cvs); } }