/*
* © 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.task;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import com.ibm.commons.util.StringUtil;
import com.ibm.commons.util.io.json.JsonException;
import com.ibm.commons.util.io.json.JsonJavaObject;
import com.ibm.sbt.provisioning.sample.app.WeightManager;
import com.ibm.sbt.provisioning.sample.app.logging.CustomLogger;
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.WeightSet;
import com.ibm.sbt.provisioning.sample.app.model.Weights;
import com.ibm.sbt.provisioning.sample.app.util.Util;
import com.ibm.sbt.provisioning.sample.app.weightedBSSCall.WeightedBSSCall;
import com.ibm.sbt.provisioning.sample.app.weightedBSSCall.authentication.ChangePassword;
import com.ibm.sbt.provisioning.sample.app.weightedBSSCall.authentication.SetOneTimePassword;
import com.ibm.sbt.provisioning.sample.app.weightedBSSCall.customer.RegisterCustomer;
import com.ibm.sbt.provisioning.sample.app.weightedBSSCall.subscriber.ActivateSubscriber;
import com.ibm.sbt.provisioning.sample.app.weightedBSSCall.subscriber.GetSubscriber;
import com.ibm.sbt.provisioning.sample.app.weightedBSSCall.subscription.CreateSubscription;
import com.ibm.sbt.provisioning.sample.app.weightedBSSCall.subscription.GetSubscription;
import com.ibm.sbt.services.client.base.JsonEntity;
import com.ibm.sbt.services.client.base.datahandlers.JsonDataHandler;
import com.ibm.sbt.services.endpoints.BasicEndpoint;
/**
* This class represents a task that triggers the provisioning of the subscribers in input to the application in the
* context of separated threads .
* <p>
* This class contains the main method executed when launching the application.
* The main method performs the necessary inizializations needed for provisioning the subscribers in input.
* The <code>run()</code> method of this class is periodically executed thanks to the singleton
* {@link com.ibm.sbt.provisioning.sample.app.WeightManager}, in order to
* permit to the partially provisioned subscriber to complete their provisioning process .
* Each parsed subscriber is represented by an instance of the class {@link SubscriberTask}, that IS-A <code>Runnable</code> too,
* and the subscriber's provisioning logic is encapsulated in the {@link SubscriberTask#run()} method .
*
* */
public class BSSProvisioning implements Runnable {
/**
* pattern used for the subscribers' state transitions timestamps
*/
public static final String DATE_FORMAT = "dd MM yyyy HH:mm:ss SSS" ;
private static final Logger logger = Logger.getLogger(BSSProvisioning.class.getName());
/**
* <code>SimpleDateFormat</code> object used for formatting
* the subscribers' state transitions' points in time
*/
private static SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
/**
* <code>BasicEndpoint</code> object representing the
* BSS Host url
*/
protected static BasicEndpoint basicEndpoint ;
/**
* path to the weights json input file
*/
private static String weightsFile ;
/**
* path to the weights data object
*/
protected static Weights weights;
/**
* boolean set to true when the default weights.json file ( that comes packaged in the jar) must be used
*/
private static boolean weightsFileAsInput = false ;
/**
* a queue containing all the {@link SubscriberTask} instances representing to the
* subscribers in input to be provisioned
*/
protected static ConcurrentLinkedQueue<SubscriberTask> subscriberTasks ;
/**
* the Executor used by the application to provision the subscribers in input
* each in their own thread , executing the <code>run()</code> method of the corresponding
* {@link SubscriberTask} instance
*/
protected static ExecutorService threadpool ;
/**
* number of subscribers in input
*/
protected static AtomicInteger subscribersQuantity;
/**
* number of currently provisioned subscribers
*/
protected static AtomicInteger subscribersProvisioned = new AtomicInteger(0);
/**
* number of currently failed subscribers
*/
protected static AtomicInteger subscribersFailed = new AtomicInteger(0);
/**
* list of failed subscribers
*/
protected static List<SubscriberTask> failedSubscriptions = new ArrayList<SubscriberTask>();
/**
* <code>Map</code> associating each subscribers with a String array, containing a timestamp for each
* state transition of the corresponding subscriber
*/
protected static Map<String,String[]> stateTransitionReport ;
/**
* <code>Map</code> associating each subscribers with a int array, containing a counter for each
* of the call made to provision the corresponding subscriber
*/
protected static Map<String,int[]> subscriberWeightReport ;
/**
* Entry point of the application
* <p>
* Initially the customer registration and the subscription creation will be triggered
* by mean of calls to the BSS api and a list of subscriber to provision will be parsed
* from an input file. For each subscriber, an instance of the {@link SubscriberTask} class, that
* implements <code>Runnable</code> too, will be created and put in the <code>static ConcurrentLinkedQueue</code> {@link #subscriberTasks}.
* Then the <code>run()</code> method will be invoked in the context of the main
* thread and the previously parsed subscribers will start to be provisioned in the
* context of separated threads.
*
* @param args Application input parameters<br>
* args[0] : BSS host url(required)<br>
* args[1] : BSS user(required)<br>
* args[2] : BSS password<br>
* args[3] : partNumber/subscriptionIdentifier<br>
* args[4] : path to the customer.json file(required)<br>
* args[5] : path to the subscribers.json file(required)<br>
* args[6] : path to the weights.json file(optional)<br>
* args[7] : number of threads in the provisioning threadpool(optional default to 10)<br>
*/
public static void main( String[] args ){
boolean logFileCreated = false ;
try {
logFileCreated = CustomLogger.setup();
} catch (IOException ioe) {
logger.severe(ioe.getMessage());
}
if(!logFileCreated){
logger.severe("No log file will be available !!!");
}
stateTransitionReport = new ConcurrentHashMap<String,String[]>();
subscriberWeightReport = new ConcurrentHashMap<String,int[]>();
if (args.length < 6 || args.length > 8) {
// Note: The email must be unique for each run of the application, it is used as the administrator email address
logger.severe("Usage: java com.ibm.sbt.provisioning.sample.app.BSSProvisioning\n "
+ "args[0] = BSS host url(required)\n "
+ "args[1] = BSS user(required)\n "
+ "args[2] = BSS password(required)\n "
+ "args[3] = partNumber/subscriptionIdentifier(required)\n"
+ "args[4] = customer.json's path(required)\n "
+ "args[5] = path to the subscribers.json file(required)\n "
+ "args[6] = path to the weights.json file(optional)\n "
+ "args[7] = provisioning threadpool size(optional default to 10)\n"
+ "The application accepts as input 4,5,7 or 8 input arguments\n");
}else{
String url = args[0];
String user = args[1];
String password = args[2];
String partNumber = args[3];
String customerFile = args[4];
String subscribersFile = args[5];
weightsFile = "/weights.json";
int threadpoolSize = 10 ;
switch( args.length ){
case 6 :
break ;
case 7 :
boolean isNotAnInt = false ;
try{
threadpoolSize = Integer.parseInt(args[6]);
}catch( NumberFormatException nfe ){
logger
.severe("NumberFormatException thrown while parsing arg[6], assuming it represents the path to the weights.json file ");
isNotAnInt = true ;
}
if(isNotAnInt){
weightsFile = args[6];
weightsFileAsInput = true ;
}
break ;
case 8 :
weightsFile = args[6];
weightsFileAsInput = true ;
try{
threadpoolSize = Integer.parseInt(args[7]);
}catch( NumberFormatException nfe ){
logger.severe("NumberFormatException thrown while parsing arg[7], using default threadpoolsize (10) ");
isNotAnInt = true ;
}
break ;
}
for(String arg :args){
System.out.println(arg);
}
List<JsonJavaObject> subscribers = null ;
subscribers = Util.parseSubscribers( subscribersFile );
if( subscribers != null && subscribers.size() > 0){
threadpool = Executors.newFixedThreadPool(threadpoolSize);
subscribersQuantity = new AtomicInteger(subscribers.size());
basicEndpoint = new BasicEndpoint();
basicEndpoint.setUrl(url);
basicEndpoint.setForceTrustSSLCertificate(true);
basicEndpoint.setUser(user);
basicEndpoint.setPassword(password);
String customerJson = null ;
try{
customerJson = Util.readFully(customerFile);
} catch (IOException ioe) {
logger.severe(ioe.getMessage());
}
if( customerJson != null ){
String orgAdminEmail = getOrgAdminEmail( customerFile ) ;
customerJson = StringUtil.replace(customerJson, "<email>", orgAdminEmail);
// CUSTOMER REGISTRATION
String customerId = registerCustomer( customerJson ) ;
if( customerId != null ){
logger.finest("customerId = "+customerId);
// ORG.ADMINISTRATOR RETRIEVAL
JsonEntity orgAdminEntity = retrieveOrgAdmin( orgAdminEmail ) ;
if( orgAdminEntity != null ){
String orgAdminId = String.valueOf(orgAdminEntity.getAsLong("Id"));
if( orgAdminId != null ){
logger.info( "org admin subscriber "+orgAdminEmail+" added with id "+orgAdminId );
// ORG.ADMINISTRATOR ACTIVATION
Boolean subscriberActive = activateOrgAdmin( orgAdminId ) ;
if( subscriberActive!= null && subscriberActive ){
// ORG.ADMINISTRATOR ONE TIME PASSWORD SETTING
Boolean oneTimePasswordSet = orgAdminOneTimePasswordSetting( orgAdminEmail , "onet1me!" ) ;
if( oneTimePasswordSet != null && oneTimePasswordSet ){
// ORG.ADMINISTRATOR PASSWORD CHANGING
Boolean passwordChanged = orgAdminChangePassword(orgAdminEmail, "onet1me!", "passw0rd") ;
if( passwordChanged != null && passwordChanged){
// SUBSCRIPTION CREATION
String subscriptionId =
createSubscription( customerId , 3, partNumber/*"D0NWLLL"*/, subscribersQuantity.get()) ;
if( subscriptionId != null ){
// sleeping 10 sec waiting for the subscription to be created and activated
try {
Thread.sleep(1000*10);
} catch (InterruptedException e) {
logger.severe(e.getMessage());
}
// SUBSCRIPTION RETRIEVAL
JsonEntity subscription = retrieveSubscription( subscriptionId ) ;
if( subscription != null ){
String currentState = subscription.getAsString("Subscription/SubscriptionState");
boolean subscriptionActive = currentState.equalsIgnoreCase("ACTIVE");
int i = 0 ;
// WAITING FOR SUBSCRIPTION TO BECOME ACTIVE
// looping ( max 20 times with 15 sec period ) till the state of the subscription is active
// sometimes it takes more than 5 min to be activated
while( !subscriptionActive && i < 20 ){
try {
Thread.sleep(1000*15);
} catch (InterruptedException e) {
logger.severe(e.getMessage());
}
subscription = retrieveSubscription( subscriptionId ) ;
if(subscription != null){
currentState = subscription.getAsString("Subscription/SubscriptionState");
subscriptionActive = currentState.equalsIgnoreCase("ACTIVE");
}else{
logger.severe("the subscription has not been retrieved !!!");
}
i++ ;
}
if(subscriptionActive){
// SUBSCRIPTION ACTIVE -->
subscriberTasks = new ConcurrentLinkedQueue<SubscriberTask>();
for( JsonJavaObject subscriber : subscribers ){
String suscriberEmail =
subscriber
.getAsObject("Subscriber")
.getAsObject("Person")
.getAsString("EmailAddress");
SubscriberTask subscriberTask =
new SubscriberTask(subscriber, customerId, subscriptionId,
SubscriberTask.State.SUBSCRIBER_NON_EXISTENT, 10);
subscriberTasks.add(subscriberTask);
stateTransitionReport.put(suscriberEmail, new String[6]);
subscriberWeightReport.put(suscriberEmail, new int[3]);
}
new BSSProvisioning().run();
}else{
logger.severe("the subscription has not been activated after 5 min waiting !!!");
WeightManager.getInstance().shutdown();
}
}else{
logger.severe("the subscription has not been retrieved !!!");
WeightManager.getInstance().shutdown();
}
}else{
logger.severe("the subscription has not been created !!!");
WeightManager.getInstance().shutdown();
}
}else{
logger.severe("org admin subscriber password has not been changed !!!");
WeightManager.getInstance().shutdown();
}
}else{
logger.severe("org admin subscriber one time password has not been set !!!");
WeightManager.getInstance().shutdown();
}
}else{
logger.severe("the org admin subscriber is not active !!!");
WeightManager.getInstance().shutdown();
}
}else{
logger.severe("org admin id is null !!!");
WeightManager.getInstance().shutdown();
}
}else{
logger.severe("could not retrive org admin subscriber !!!");
WeightManager.getInstance().shutdown();
}
}else{
logger.severe("customer has not been registered !!!");
WeightManager.getInstance().shutdown();
}
}else{
logger.severe("No customer to be created !!!");
WeightManager.getInstance().shutdown();
}
}else{
logger.severe("No subscribers to provison !!!");
WeightManager.getInstance().shutdown();
}
}
}
/**
* Business logic of the task
* <p>
* This method will simply poll instances of the {@link SubscriberTask} class from the
* <code>static ConcurrentLinkedQueue</code> {@link #subscriberTasks}
* and submit them for execution to the <code>static ExecutorService</code> named {@link #threadpool}.
* This <code>Executor</code> represents a thread pool that reuses
* a fixed number of threads operating off a shared unbounded internal queue. If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available. Every time the <code>submit()</code> method is invoked on
* the <code>static</code> {@link #threadpool} object, the <code>run()</code> method of the corresponding {@link SubscriberTask}
* instance, will be executed in the context of a separated thread of execution.
*/
@Override
public void run(){
try{
logger.info("Provisioning of users... " + BSSProvisioning.getSubscribersProvisioned().get() + "/" + BSSProvisioning.getSubscribersQuantity().get());
// first execution in the context of the main thread, all the others in their own thread
if(!Thread.currentThread().getName().equals("main")){
Thread.currentThread().setName("Provisioning");
}
if(BSSProvisioning.getSubscribersQuantity().get() > (BSSProvisioning.getSubscribersProvisioned().get() + BSSProvisioning.getSubscribersFailed().get())){
// stop submitting when the threshold has been reached and the queue has been emptied
while( !WeightManager.getInstance().isThresholdReached() && subscriberTasks != null && !subscriberTasks.isEmpty() ){
SubscriberTask subscriberTask = subscriberTasks.poll();
if( subscriberTask != null ){
logger.info("Flow : "+subscriberTask.getSubscriberTaskKey()+" and status " + subscriberTask.getStatus().name()+" polled from the queue...submitting it...");
if( subscriberTask.getStatus() == SubscriberTask.State.SUBSCRIBER_NON_EXISTENT ){
String[] subscriberReport = stateTransitionReport.get(subscriberTask.getSubscriberEmail());
subscriberReport[0] = sdf.format(new Date());
}
threadpool.submit(subscriberTask);
if(subscriberTask.getStatus() == SubscriberTask.State.SUBSCRIBE_FAILED){
logger.info("Subscriber task for " +subscriberTask.getSubscriberId()+ " failed due to exceeded attempts");
}
}
//sleeping 250 millisec between one submit and the other with the goal of
//avoiding to overwhelm the server with requests
Thread.sleep(250);
}
}else{
if(BSSProvisioning.getSubscribersFailed().get()>0){
logger.warning("Provisioned Subscribers: "+ BSSProvisioning.getSubscribersProvisioned().get() +"\nFailed Subscribers: "+ BSSProvisioning.getSubscribersFailed().get());
}else{
logger.finest("ALL SUBSCRIBERS PROVISIONED !!!");
}
logger.info("Provisioning finished");
WeightManager.getInstance().shutdown();
}
}catch(Exception e){
logger.severe("Catched Exception in the BSSProvisioning run() method : "+e.getMessage() );
}
}
/**
* This method will trigger the customer registration by mean of invocation of the
* {@link com.ibm.sbt.provisioning.sample.app.weightedBSSCall.WeightedBSSCall#call()} method
* on an instance of the {@link com.ibm.sbt.provisioning.sample.app.weightedBSSCall.customer.RegisterCustomer} class
* <p>
* @param customerJson json formatted string representing the customer to be created<br>
* @return the BSS customer identifier
*/
private static String registerCustomer( String customerJson ){
String customerId = null ;
logger.info("Customer registration ...");
WeightedBSSCall<String> registerCustomer = new RegisterCustomer(customerJson);
try{
customerId = registerCustomer.call();
} catch (Exception e) {
logger.severe(e.getClass()+" : " + e.getMessage());
}
return customerId ;
}
/**
* This method will trigger the organization administrator retrieval by mean of invocation of the
* {@link com.ibm.sbt.provisioning.sample.app.weightedBSSCall.WeightedBSSCall#call()} method
* on an instance of the {@link com.ibm.sbt.provisioning.sample.app.weightedBSSCall.subscriber.GetSubscriber} class
* <p>
* @param email email used as user credential of the organization administrator<br>
* @return a <code>JsonEntity</code> representing the retrieved organization administrator
*/
private static JsonEntity retrieveOrgAdmin( String email ){
JsonEntity orgAdminEntity = null ;
logger.info("Org administrator retrieval...");
WeightedBSSCall<JsonEntity> getSubscriber = new GetSubscriber( email, null );
try {
orgAdminEntity = getSubscriber.call() ;
} catch (Exception e) {
logger.severe(e.getClass()+" : " + e.getMessage());
}
return orgAdminEntity ;
}
/**
* This method will trigger the organization administrator activation by mean of invocation of the
* {@link com.ibm.sbt.provisioning.sample.app.weightedBSSCall.WeightedBSSCall#call()} method
* on an instance of the {@link com.ibm.sbt.provisioning.sample.app.weightedBSSCall.subscriber.ActivateSubscriber} class
* <p>
* @param orgAdminId BSS identifier of the organization administrator<br>
* @return <code>true</code> if activated, <code>false</code> otherwise
*/
private static Boolean activateOrgAdmin( String orgAdminId ){
Boolean subscriberActive = false ;
logger.info("Org administrator activation...");
WeightedBSSCall<Boolean> activateSubscriber = new ActivateSubscriber(orgAdminId);
try{
subscriberActive = activateSubscriber.call();
} catch (Exception e) {
logger.severe(e.getClass()+" : " + e.getMessage());
}
return subscriberActive ;
}
/**
* This method will trigger the organization administrator one time password setting by mean of invocation of the
* {@link com.ibm.sbt.provisioning.sample.app.weightedBSSCall.WeightedBSSCall#call()} method
* on an instance of the {@link com.ibm.sbt.provisioning.sample.app.weightedBSSCall.authentication.SetOneTimePassword} class
* <p>
* @param email email used as user credential of the organization administrator<br>
* @param oneTimePassword string representing the one time password<br>
* @return <code>true</code> if the one time password is set, <code>false</code> otherwise
*/
private static Boolean orgAdminOneTimePasswordSetting( String email , String oneTimePassword ){
Boolean oneTimePasswordSet = false ;
logger.info("Org administrator one time password setting...");
WeightedBSSCall<Boolean> setOneTimePassword = new SetOneTimePassword(email , oneTimePassword );
try{
oneTimePasswordSet = setOneTimePassword.call();
}catch (Exception e) {
logger.severe(e.getClass()+" : " + e.getMessage());
}
return oneTimePasswordSet ;
}
/**
* This method will trigger the organization administrator password changing by mean of invocation of the
* {@link com.ibm.sbt.provisioning.sample.app.weightedBSSCall.WeightedBSSCall#call()} method
* on an instance of the {@link com.ibm.sbt.provisioning.sample.app.weightedBSSCall.authentication.ChangePassword} class
* <p>
* @param email email used as user credential of the organization administrator<br>
* @param oneTimePassword string representing the one time password<br>
* @param newPassword string representing the new password<br>
* @return <code>true</code> if the password is changed, <code>false</code> otherwise
*/
private static Boolean orgAdminChangePassword( String email , String oneTimePassword , String newPassword ){
Boolean passwordChanged = false ;
logger.info("Org administrator changing password ...");
WeightedBSSCall<Boolean> changePassword = new ChangePassword(email, oneTimePassword, newPassword);
try{
passwordChanged = changePassword.call();
}catch (Exception e) {
logger.severe(e.getClass()+" : " + e.getMessage());
}
return passwordChanged ;
}
/**
* This method will trigger the subscription creation by mean of invocation of the
* {@link com.ibm.sbt.provisioning.sample.app.weightedBSSCall.WeightedBSSCall#call()} method
* on an instance of the {@link com.ibm.sbt.provisioning.sample.app.weightedBSSCall.subscription.CreateSubscription} class
* <p>
* @param customerId the BSS customer identifier<br>
* @param duration an int representing the subscription duration in years<br>
* @param partNumber string representing the subscription identifier<br>
* @param quantity an int representing the number of seat of the subscription<br>
* @return the BSS subscription identifier
*/
private static String createSubscription( String customerId , int duration , String partNumber, int quantity ){
String subscriptionId = null ;
logger.info("Subscription creation ...");
WeightedBSSCall<String> createSubscription = new CreateSubscription(customerId, duration, partNumber, quantity);
try {
subscriptionId = createSubscription.call();
}catch (Exception e) {
logger.severe(e.getClass()+" : " + e.getMessage());
}
return subscriptionId ;
}
/**
* This method will trigger the subscription retrieval by mean of invocation of the
* {@link com.ibm.sbt.provisioning.sample.app.weightedBSSCall.WeightedBSSCall#call()} method
* on an instance of the {@link com.ibm.sbt.provisioning.sample.app.weightedBSSCall.subscription.GetSubscription} class
* <p>
* @param subscriptionId the BSS subscription identifier<br>
* @return a <code>JsonEntity</code> representing the retrieved subscription
*/
private static JsonEntity retrieveSubscription( String subscriptionId ){
JsonEntity subscription = null ;
logger.info("Subscription retrieval ...");
WeightedBSSCall<JsonEntity> getSubscription = new GetSubscription(subscriptionId);
try {
subscription = getSubscription.call();
}catch (Exception e) {
logger.severe(e.getClass()+" : " + e.getMessage());
}
return subscription ;
}
/**
* CSV report generation
* <p>
* This method uses the {@link #stateTransitionReport} <code>Map</code> for the generation of a CSV report
* tracing the lifecycle of each subscriber
*/
public static void generateStateTransitionReport(){
try{
File csvReport = new File("stateTransitionReport.csv");
BufferedWriter bwSub = new BufferedWriter(new FileWriter(csvReport));
bwSub
.write("Email,SUBSCRIBER_NON_EXISTENT,SUBSCRIBER_ADDED,SUBSCRIBER_ACTIVE,SUBSCRIBER_ONE_TIME_PWD_SET,SUBSCRIBER_ENTITLED,SEAT_ASSIGNED,TOTAL TIME(sec)\n");
for(String emailSubscriptionId:stateTransitionReport.keySet()){
long seatAssigned = 0L ;
long subscriberNonExistent = 0L ;
String[] timestamps = stateTransitionReport.get(emailSubscriptionId);
bwSub.write(emailSubscriptionId + ",");
int index = 0 ;
for(String timestamp : timestamps){
if( index == 0 ){
try {
subscriberNonExistent = (sdf.parse(timestamp)).getTime();
} catch (ParseException e) {
logger.severe("Unparsable SUBSCRIBER_NON_EXISTENT timestamp :\n" + e.getMessage() );
}
}else if(index == 5){
try {
seatAssigned = (sdf.parse(timestamp)).getTime();
} catch (ParseException e) {
logger.severe("Unparsable SEAT_ASSIGNED timestamp :\n" + e.getMessage() );
}
}
if( timestamp == null){
bwSub.write("null,");
}else{
bwSub.write(timestamp+",");
}
index++;
}
if(subscriberNonExistent != 0L && seatAssigned != 0L ){
long totalProvisioningTime = (seatAssigned - subscriberNonExistent)/1000 ;
bwSub.write(totalProvisioningTime+"\n");
}else{
bwSub.write("null\n");
}
}
bwSub.flush();
bwSub.close();
}catch( IOException ioe ){
ioe.printStackTrace();
}
}
/**
* CSV report generation
* <p>
* This method uses {@link #subscriberWeightReport} <code>Map</code> for the generation of a CSV report
* tracing the weight consumed by each subscriber
*/
public static void generateSubscriberWeightReport(){
try{
File csvReport = new File("subscriberWeightReport.csv");
BufferedWriter bwSub = new BufferedWriter(new FileWriter(csvReport));
bwSub.write("Email,/resource/subscriber:POST,/service/authentication,/resource/subscriber:GET,TOTAL_WEIGHT\n");
for(String emailSubscriptionId:subscriberWeightReport.keySet()){
int totalWeight = 0 ;
int[] callNumbers = subscriberWeightReport.get(emailSubscriptionId);
bwSub.write(emailSubscriptionId + ",");
int index = 0 ;
for(int callNumber : callNumbers){
switch( index ){
case 0 :
totalWeight =
totalWeight
+ (callNumber * WeightManager.getInstance().getWeightValuePerBSSCall("/resource/subscriber",
Rest.POST));
bwSub.write(callNumber+",");
break;
case 1 :
totalWeight =
totalWeight
+ (callNumber * WeightManager.getInstance().getWeightValuePerBSSCall("/service/authentication",
Rest.ALL));
bwSub.write(callNumber+",");
break;
case 2 :
totalWeight =
totalWeight
+ (callNumber * WeightManager.getInstance().getWeightValuePerBSSCall("/resource/subscriber",
Rest.GET));
bwSub.write(callNumber+","+totalWeight+"\n");
break;
}
index++;
}
}
bwSub.flush();
bwSub.close();
}catch( IOException ioe ){
ioe.printStackTrace();
}
}
/**
* CSV report generation
* <p>
* This method uses a map having as keys the BSS calls keys as specified by the
* {@link com.ibm.sbt.provisioning.sample.app.weightedBSSCall.BSSCall} <code>interface</code>
* and as values the number of times that endpoint has been accessed using
* that HTTP method, for the generation of a CSV report keeping track of the BSS API usage
*/
public static void generateCallsCounterReport(){
try{
File csvReport = new File("callsCounterReport.csv");
BufferedWriter bwSub = new BufferedWriter(new FileWriter(csvReport));
String header = "" ;
String row = "" ;
WeightSet defaultSet = weights.getDefaultSet();
for(String url:defaultSet.getUrls()){
HashMap<Rest, Weight> methodMap = defaultSet.getAllFromUrl(url);
for(Rest method:methodMap.keySet()){
Weight weight = methodMap.get(method);
header = header + url + ":" + method + ",";
row = row + weight.getCounter() + ",";
}
}
header = ( header.substring( 0, header.length() -1 ) ) + "\n" ;
row = ( row.substring( 0, row.length() -1 ) ) + "\n" ;
logger.info(header);
logger.info(row);
bwSub.write(header);
bwSub.write(row);
bwSub.flush();
bwSub.close();
}catch( IOException ioe ){
ioe.printStackTrace();
}
}
/**
* {@link #basicEndpoint} getter method
*/
public static BasicEndpoint getBasicEndpoint() {
return basicEndpoint;
}
/**
* {@link #weightsFile} getter method
*/
public static String getWeightsFile() {
return weightsFile;
}
/**
* {@link #weights} getter method
*/
public static Weights getWeights(){
return weights;
}
/**
* {@link #weightsFileAsInput} getter method
*/
public static boolean isWeightsFileAsInput() {
return weightsFileAsInput;
}
/**
* {@link #subscriberTasks} getter method
*/
public static ConcurrentLinkedQueue<SubscriberTask> getSubscribersTasks() {
return subscriberTasks;
}
/**
* {@link #threadpool} getter method
*/
public static ExecutorService getThreadPool() {
return threadpool;
}
/**
* {@link #subscribersQuantity} getter method
*/
public static AtomicInteger getSubscribersQuantity(){
return subscribersQuantity;
}
/**
* {@link #subscribersProvisioned} incrementation method
*/
public static synchronized void incrementSubscribersProvisioned(){
subscribersProvisioned.incrementAndGet();
}
/**
* {@link #subscribersProvisioned} getter method
*/
public static synchronized AtomicInteger getSubscribersProvisioned(){
return subscribersProvisioned ;
}
/**
* {@link #subscribersFailed} incrementation method
*/
public static synchronized void incrementSubscribersFailed(){
subscribersFailed.incrementAndGet();
}
/**
* {@link #subscribersFailed} getter method
*/
public static synchronized AtomicInteger getSubscribersFailed(){
return subscribersFailed ;
}
/**
* {@link #failedSubscriptions} getter method
*/
public static synchronized List<SubscriberTask> getFailedSubscriptions(){
return failedSubscriptions ;
}
/**
* {@link #stateTransitionReport} getter method
*/
public static Map<String, String[]> getStateTransitionReport() {
return stateTransitionReport;
}
/**
* {@link #subscriberWeightReport} getter method
*/
public static Map<String, int[]> getSubscriberWeightReport() {
return subscriberWeightReport;
}
/**
* This method will parse the json input file representing the customer in order to return the email address of the
* organization administrator
*
* @return a String representing the organization administrator email
*/
private static String getOrgAdminEmail( String customerFilePath ){
String orgAdminEmail = null ;
String customerJson = null ;
JsonDataHandler handler = null ;
try{
customerJson = Util.readFully(customerFilePath);
handler = new JsonDataHandler(customerJson);
}catch( IOException ioe ){
logger.severe("Exception thrown during customer.json file parsing : "+ ioe.getMessage());
}catch (JsonException e) {
logger.severe("Exception thrown during customer.json file parsing : "+ e.getMessage());
}
if( customerJson != null && handler != null ) {
orgAdminEmail = handler.getAsString("Customer/Organization/Contact/EmailAddress");
logger.finest("Org admin email :"+orgAdminEmail);
}
return orgAdminEmail ;
}
}