/*
* © Copyright IBM Corp. 2014
*
* 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 com.ibm.sbt.provisioning.sample.app;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import com.ibm.sbt.provisioning.sample.app.model.Rest;
import com.ibm.sbt.provisioning.sample.app.model.Weight;
import com.ibm.sbt.provisioning.sample.app.model.Weights;
import com.ibm.sbt.provisioning.sample.app.task.BSSProvisioning;
import com.ibm.sbt.provisioning.sample.app.util.ShutdownCommand;
/**
* This class represents a singleton responsible for managing the current weight associated with the organization .
* */
public class WeightManager {
private static final Logger logger = Logger.getLogger(WeightManager.class.getName());
private static WeightManager instance = null;
private static ShutdownCommand command;
/**
* An int keeping track of the load generated by a customer towards the BSS API
* while provisioning its subscribers
* */
private int currentWeight = 0 ;
/**
* A boolean that evaluates to true when the load {@link #threshold} for the customer in input has been reached
* */
private boolean thresholdReached ;
/**
* The amount of time in millisecond that separates each {@link #currentWeight} reset operation
* */
private long resetDuration ;
private long maxSystemWeight ;
/**
* An int representing the load threshold for a customer
* */
private long threshold ;
protected ScheduledExecutorService resetterExec;
/**
* <code>Executor</code> needed for periodically submit he {@link com.ibm.sbt.provisioning.sample.app.task.SubscriberTask}
* instances to the provisioning threadpool ( {@link com.ibm.sbt.provisioning.sample.app.task.BSSProvisioning#getThreadPool()} ) .
* */
protected ScheduledExecutorService bssProvExec;
public static synchronized WeightManager getInstance() {
if(instance == null) {
instance = new WeightManager(BSSProvisioning.getWeights());
}
return instance;
}
/**
* Allows to set an arbitrary command to be executed at shutdown time
* @param shutdownCommand
*/
public static void setCommand(ShutdownCommand shutdownCommand){
command = shutdownCommand;
}
/**
* Constructor responsible for the {@link #resetterExec} and {@link #bssProvExec} <code>Executor</code>(s)
* initialization, for the weights json input file parsing and loading of its content in the {@link #weightPerBSSCall}
* <code>Map</code>, and initialization of the {@link #resetDuration}, {@link #maxSystemWeight} and
* {@link #threshold} fields
* */
private WeightManager(Weights weights){
this.resetterExec = Executors.newSingleThreadScheduledExecutor();
this.bssProvExec = Executors.newSingleThreadScheduledExecutor();
this.threshold = weights.getLimit();
// number of seconds needed for scheduling the BSSProvisioning.getSubscribersTasks() queue +
// ( approximate time for the initial customer and subscription creation and activation + 30sec )
long firstBSSProvisioningIt = (((long)BSSProvisioning.getSubscribersQuantity().get())/4L)*1000L + 60000L ;
this.resetDuration = weights.getResetDuration();
this.resetterExec.scheduleAtFixedRate( new Resetter(), this.resetDuration , this.resetDuration, TimeUnit.MILLISECONDS );
this.bssProvExec.scheduleAtFixedRate( new BSSProvisioning(), firstBSSProvisioningIt , 30000L, TimeUnit.MILLISECONDS );
}
/**
* This class represents a task that will periodically executed every
* {@link #resetDuration} millisecond and when executed it will reset the {@link #currentWeight}
* */
class Resetter implements Runnable {
/**
* Business logic of the task
* <p>
* It will simply call the {@link #resetCurrentWeight} method
* */
@Override
public void run(){
Thread.currentThread().setName("Resetter");
logger.finest("Resetting current weight...");
WeightManager.this.resetCurrentWeight();
}
}
/**
* This <code>synchronized</code> method simply reset the {@link #currentWeight} and
* set the {@link #thresholdReached} value to <code>false</code>
* */
private synchronized void resetCurrentWeight(){
this.currentWeight = 0 ;
WeightManager.this.thresholdReached = false ;
}
/**
* This method will update the {@link #currentWeight} associated with the organization depending
* on the call being made.
* <p>
*
* @param url
* @param method
* @return <code>true</code> if the call is permitted because the {@link #threshold} has not been
* reached after the {@link #currentWeight} update , <code>false</code> otherwise
*/
public synchronized boolean updateCurrentWeight(String url, Rest method){
boolean callPermitted = true ;
if( !thresholdReached ){
logger.finest("currentWeight = " + this.currentWeight);
this.currentWeight = this.currentWeight + getWeightValuePerBSSCall(url, method);
logger.finest("currentWeight updated = " + this.currentWeight);
logger.finest("Threshold = " + threshold);
if( currentWeight >= threshold ){
callPermitted = false ;
logger.warning("THRESHOLD REACHED OR EXCEEDED !!!");
this.thresholdReached = true ;
}else{
this.incrementCounterPerBSSCall(url, method, 1);
}
}else{
callPermitted = false ;
}
return callPermitted ;
}
/**
* {@link #resetDuration} getter method
*/
public long getResetDuration() {
return resetDuration;
}
/**
* {@link #maxSystemWeight} getter method
*/
public long getMaxSystemWeight() {
return maxSystemWeight;
}
/**
* {@link #threshold} getter method
*/
public long getThreshold() {
return threshold;
}
/**
* {@link #thresholdReached} getter method
*/
public synchronized boolean isThresholdReached() {
return thresholdReached;
}
/**
* Shuts down the ExecutorServices
*/
public void shutdown(){
if(!this.resetterExec.isShutdown()){
this.resetterExec.shutdownNow();
}
if(!this.bssProvExec.isShutdown()){
this.bssProvExec.shutdownNow();
}
if(!BSSProvisioning.getThreadPool().isShutdown()){
BSSProvisioning.getThreadPool().shutdown();
}
if(command != null){
command.execute();
}
}
/**
* weightPerBSSCall getter method
*/
public Weight getWeightPerBSSCall(String url, Rest method){
return BSSProvisioning.getWeights().getWeight(url, method);
}
public int getWeightValuePerBSSCall(String url, Rest method){
return BSSProvisioning.getWeights().getWeightValue(url, method);
}
public int incrementCounterPerBSSCall(String url, Rest method, int amount){
return BSSProvisioning.getWeights().incrementCounter(url, method, amount);
}
}