/*
* Copyright (c) 2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.carbon.usage.agent.persist;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.usage.agent.beans.BandwidthUsage;
import org.wso2.carbon.usage.agent.exception.UsageException;
import org.wso2.carbon.usage.agent.util.PublisherUtils;
import java.util.Collection;
import java.util.HashMap;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
public class PersistenceManager {
private static Log log = LogFactory.getLog(PersistenceManager.class);
// queue to store Bandwidth usage statistics.
// usage of LinkedBlockingQueue ensures operations on the queue to wait for the queue to be non
// empty when retrieving and wait for space when storing element.
private Queue<BandwidthUsage> jobQueue = new LinkedBlockingQueue<BandwidthUsage>();
/**
* this method instantiates and starts a thread to persist bandwidth usage statistics
*/
public void startPersisting() {
new Thread() {
public void run() {
// thread runs as long as it is not interrupted and sleeps for 10 seconds
while (true) {
// check whether the jobQueue is not empty
if (!jobQueue.isEmpty()) {
try {
// if JobQueue is not empty persist usage
persistUsage();
} catch (UsageException e) {
log.error("Error when persisting bandwidth usage statistics", e);
}
}
// return from while loop if the thread is interrupted
if (isInterrupted()) {
break;
}
// let the thread sleep for 10 seconds
try {
sleep(10 * 1000);
} catch (InterruptedException e) {
if (jobQueue == null || jobQueue.isEmpty()) {
break;
}
// Restoring the interrupted status after catching InterruptedException
// instead of Swallowing
Thread.currentThread().interrupt();
}
}
}
}.start();
}
/**
* this method add bandwidth usage entries to the jobQueue
*
* @param usage Bandwidth usage
*/
public void addToQueue(BandwidthUsage usage) {
jobQueue.add(usage);
}
/**
* this method create a Summarizer object for each tenant and call accumulate() method to
* accumulate usage statistics
*
* @throws UsageException
*/
public void persistUsage() throws UsageException {
// create a map to hold summarizer objects against tenant id
HashMap<Integer, Summarizer> summarizerMap = new HashMap<Integer, Summarizer>();
// if the jobQueue is not empty
while (!jobQueue.isEmpty()) {
// get the first element from the queue, which is a BandwidthUsage object
BandwidthUsage usage = jobQueue.poll();
// get the tenant id
int tenantId = usage.getTenantId();
//get the Summarizer object corresponds to the tenant id
Summarizer summarizer = summarizerMap.get(tenantId);
// when tenant invoke service for the first time, no corresponding summarizer object in
// the map
if (summarizer == null) {
//create a Summarizer object and put to the summarizerMap
summarizer = new Summarizer();
summarizerMap.put(tenantId, summarizer);
}
// now accumulate usage
summarizer.accumulate(usage);
}
//Finished accumulating. Now publish the events
// get the collection view of values in summarizerMap
Collection<Summarizer> summarizers = summarizerMap.values();
// for each summarizer object call the publish method
for (Summarizer summarizer : summarizers) {
summarizer.publish();
}
}
/**
* inner class Summarizer
* this class is used to accumulate and publish usage statistics.
* for each tenant this keeps a map to store BandwidthUsage values
*/
private static class Summarizer {
private HashMap<String, BandwidthUsage> usageMap;
public Summarizer() {
usageMap = new HashMap<String, BandwidthUsage>();
}
/**
* the method to accumulate usage data
*
* @param usage BandwidthUsage
*/
public void accumulate(BandwidthUsage usage) {
// get the measurement name of usage entry
String key = usage.getMeasurement();
// get the existing value of measurement
BandwidthUsage existingUsage = usageMap.get(key);
// if this measurement is metered earlier add the new value to the existing value
if (existingUsage != null) {
existingUsage.setValue(existingUsage.getValue() + usage.getValue());
} else {
// if this measurement is not metered previously we need to add it to the usageMap
usageMap.put(key, usage);
}
}
/**
* this method reads usage items from the usageMap and call publish method to publish to
* the BAM
*
* @throws UsageException
*/
public void publish() throws UsageException {
// get the collection view of values in usageMap
Collection<BandwidthUsage> usages = usageMap.values();
for (BandwidthUsage usage : usages) {
try {
// publish the usage entry
PublisherUtils.publish(usage);
} catch (UsageException e) {
log.error("Error in publishing bandwidth usage data", e);
}
}
}
}
}