/******************************************************************************* * ALMA - Atacama Large Millimeter Array * Copyright (c) ESO - European Southern Observatory, 2011 * (in the framework of the ALMA collaboration). * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *******************************************************************************/ package alma.acs.service; import gov.sandia.CosNotification.NotificationServiceMonitorControl; import gov.sandia.CosNotification.NotificationServiceMonitorControlPackage.InvalidName; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.logging.Logger; import alma.acs.exceptions.AcsJException; import alma.acs.logging.AcsLogLevel; import alma.acs.nsstatistics.ChannelData; import alma.acs.nsstatistics.EventData; import alma.acs.nsstatistics.EventModel; import alma.acs.nsstatistics.MCStatistics; import alma.acs.nsstatistics.NotifyServiceData; import alma.acs.nsstatistics.NotifyServices; import alma.acs.service.ChannelStats; /** * @author pcolomer * * $Id: ncStatisticsService.java,v 1.39 2014/11/17 15:36:44 pcolomer Exp $ */ public class nsStatisticsService extends Thread { /** * Possible status of services and channels */ protected enum Status { ENABLED, DISABLED, UNKNOWN } class ChannelInfo { public Status status; public ChannelStats stats; public ChannelInfo() { status = Status.UNKNOWN; stats = new ChannelStats(); } public ChannelInfo(Status status) { this.status = status; stats = new ChannelStats(); } } class ServiceInfo { public Status status; public HashMap<String,ChannelInfo> channels; public ServiceInfo() { status = Status.UNKNOWN; channels = new HashMap<String,ChannelInfo>(); } public ServiceInfo(Status status) { this.status = status; channels = new HashMap<String,ChannelInfo>(); } } /** * Tool name used to identify log messages */ static final String TOOL_NAME = "nsStatisticsService"; /** * Statistics acquisition frequency in milliseconds */ static final long ACQUISITION_FREQUENCY = 1 * ServiceParameters.MIN_2_MS; // 1min /** * Main loop frequency in milliseconds */ static final long MAIN_LOOP_FREQUENCY = 1000; // 1s /** * Event model used to get services & channels statistics */ protected EventModel eventModel; /** * Command line parameters */ protected ServiceParameters params; /** * Logger */ protected Logger logger; /** * Attribute used to decide when to stop the execution of this tool */ protected boolean stop; /** * Status of services and channels */ protected HashMap<String,ServiceInfo> status; /** * Constructor * @param eventModel Event model used to get statistics of services and channels * @param params Command line parameters */ public nsStatisticsService(EventModel eventModel,ServiceParameters params) { this.params = params; this.eventModel = eventModel; this.logger = eventModel.getLogger(); this.stop = false; initStatus(); } protected void initStatus() { status = new HashMap<String,ServiceInfo>(); HashMap<String,String[]> data = params.getSelectedServicesChannels(); Set<String> services = data.keySet(); for(Iterator<String> it = services.iterator();it.hasNext();) { String service = it.next(); String [] channels = data.get(service); ServiceInfo serviceInfo = new ServiceInfo(); if(channels != null) { for(int i = 0;i < channels.length;++i) { serviceInfo.channels.put(service + "#" + channels[i], new ChannelInfo()); } } status.put(service, serviceInfo); } } /** * Set the status passed as parameter to all services * @param status */ protected void setServicesStatus(Status status) { Set<String> registeredServices = this.status.keySet(); for(Iterator<String> it = registeredServices.iterator();it.hasNext();) { String serviceName = it.next(); ServiceInfo serviceInfo = this.status.get(serviceName); if(serviceInfo.status != status) { serviceInfo.status = status; if(Status.ENABLED == status) { logger.warning("Notify Service '" + serviceName + "' is running"); } else if(Status.DISABLED == status) { logger.warning("Notify Service '" + serviceName + "' has been stopped"); } else if(Status.UNKNOWN == status) { logger.warning("Notify Service '" + serviceName + "' status is unknown"); } } } } protected void updateStatus(List<NotifyServiceData> services,List<ChannelData> channels) { NotifyServiceData service = null; ChannelData channel = null; // Iterate services passed as input parameter to update their status for(Iterator<NotifyServiceData> it = services.iterator();it.hasNext();) { service = it.next(); // Status of the current service already exists if(status.containsKey(service.getName())) { ServiceInfo serviceInfo = status.get(service.getName()); if(Status.DISABLED == serviceInfo.status) { logger.warning("Notify Service '"+service.getName()+"' has been restarted"); serviceInfo.status = Status.ENABLED; } else if(Status.UNKNOWN == serviceInfo.status) { logger.warning("Notify Service '"+service.getName()+"' is running"); serviceInfo.status = Status.ENABLED; } // Status of the current service didn't exist so we create it } else { status.put(service.getName(), new ServiceInfo(Status.ENABLED)); logger.warning("Notify Service '"+service.getName()+"' is running"); } } // Iterate channels passed as input parameter to update their status for(Iterator<ChannelData> it = channels.iterator();it.hasNext();) { channel = it.next(); String channelName = channel.getQualifiedName(); String serviceName = channel.getParent().getName(); ServiceInfo serviceInfo = null; if(status.containsKey(serviceName)) { serviceInfo = status.get(serviceName); // Status of the service of the current channel doesn't exist so we create it } else { serviceInfo = new ServiceInfo(Status.ENABLED); status.put(serviceName, serviceInfo); logger.warning("Notify Service '"+serviceName+"' is running"); } ChannelInfo channelInfo = null; if(serviceInfo.channels.containsKey(channelName)) { channelInfo = serviceInfo.channels.get(channelName); if(Status.DISABLED == channelInfo.status) { channelInfo.status = Status.ENABLED; } else if(Status.UNKNOWN == channelInfo.status) { channelInfo.status = Status.ENABLED; } } else { channelInfo = new ChannelInfo(Status.ENABLED); serviceInfo.channels.put(channelName, channelInfo); } // Update channel statistics channelInfo.stats.setData(channel); } // Iterate services and channels already registered to disable the ones // that are not found Set<String> registeredServices = status.keySet(); for(Iterator<String> it = registeredServices.iterator();it.hasNext();) { String serviceName = it.next(); ServiceInfo serviceInfo = status.get(serviceName); boolean found = false; for(Iterator<NotifyServiceData> it2 = services.iterator();it2.hasNext() && false == found;) { service = it2.next(); if(service.getName().equals(serviceName)) { found = true; } } if(false == found && Status.ENABLED == serviceInfo.status) { logger.warning("Notify Service '" + serviceName + "' has been stopped"); serviceInfo.status = Status.DISABLED; } Set<String> registeredChannels = serviceInfo.channels.keySet(); for(Iterator<String> itc = registeredChannels.iterator();itc.hasNext();) { String channelName = itc.next(); ChannelInfo channelInfo = serviceInfo.channels.get(channelName); found = false; for(Iterator<ChannelData> itc2 = channels.iterator();itc2.hasNext() && false == found;) { channel = itc2.next(); if(channel.getQualifiedName().equals(channelName)) { found = true; } } if(false == found && Status.ENABLED == channelInfo.status) { channelInfo.status = Status.DISABLED; } } } } protected void logStatus() { Set<String> registeredServices = status.keySet(); for(Iterator<String> it = registeredServices.iterator();it.hasNext();) { String serviceName = it.next(); ServiceInfo serviceInfo = status.get(serviceName); Set<String> registeredChannels = serviceInfo.channels.keySet(); String chsNames = ""; int nChannels = 0; for(Iterator<String> itc = registeredChannels.iterator();itc.hasNext();) { String channelName = itc.next(); ChannelInfo channelInfo = serviceInfo.channels.get(channelName); //if(channelInfo.status == Status.ENABLED) { Map<String,String> values = channelInfo.stats.getInfoParams(params); logger.log(AcsLogLevel.INFO, "STATISTICS OF NOTIFICATION CHANNEL " + channelName, values); values = channelInfo.stats.getDbgParams(); logger.log(AcsLogLevel.DEBUG, "STATISTICS OF NOTIFICATION CHANNEL " + channelName, values); //} if(channelInfo.status == Status.ENABLED) { chsNames += channelName + ", "; nChannels += 1; } } HashMap<String,String> params = new HashMap<String,String>(); if(nChannels > 0) { params.put("Active event channels", chsNames); } params.put("Num active event channels", String.valueOf(nChannels)); logger.log(AcsLogLevel.INFO, "STATISTICS OF NOTIFICATION FACTORY " + serviceName, params); } } /** * Should statistics of channel channelName be logged? * @param serviceName Name of service which owns the channel channelName * @param channelName Name of channel * @return */ protected boolean shouldLogChannel(String serviceName,String channelName) { boolean log = false; String [] selChannels = null; HashMap<String,String[]> selServicesChannels = params.getSelectedServicesChannels(); if(selServicesChannels.containsKey(serviceName)) { selChannels = selServicesChannels.get(serviceName); if(selChannels.length == 0) { log = true; } else { for(int i = 0;i < selChannels.length;++i) { if(selChannels[i].equals(channelName)) { log = true; } } } } else if(selServicesChannels.size() == 0) { log = true; } return log; } /** * Get current services to be treated * @return List of services */ protected List<NotifyServiceData> getCurrentServices() { NotifyServices ns = eventModel.getNotifyServicesRoot(); List<NotifyServiceData> selServices = new ArrayList<NotifyServiceData>(); List<NotifyServiceData> services = ns.getServices(); if(params.getSelectedServicesChannels().isEmpty()) { return services; } else { NotifyServiceData service = null; for(Iterator<NotifyServiceData> it = services.iterator();it.hasNext();) { service = it.next(); if(params.getSelectedServicesChannels().containsKey(service.getName())) { selServices.add(service); } } } return selServices; } /** * Get current channels to be treated * @param services */ protected List<ChannelData> getCurrentChannels(NotifyServices ns) { ChannelData channel = null; List<ChannelData> channels = ns.getAllChannels(); List<ChannelData> selChannels = new ArrayList<ChannelData>(); for(Iterator<ChannelData> it = channels.iterator();it.hasNext();) { channel = it.next(); if(shouldLogChannel(channel.getParent().getName(), channel.getName())) { selChannels.add(channel); } } return selChannels; } /** * */ public void run() { long acqFreq = ACQUISITION_FREQUENCY; // 1min long logFreq = params.getFrequency(); // at least 1min if(logFreq < acqFreq) { logger.log(AcsLogLevel.WARNING, "Statistics of Notification Services will be obtained with a very high frequency (less than 1 minute time interval)"); acqFreq = logFreq; } logger.log(AcsLogLevel.INFO, "Acquisition frequency: " + String.valueOf(acqFreq) + "ms"); logger.log(AcsLogLevel.INFO, "Log frequency: " + String.valueOf(logFreq) + "ms"); acqFreq = acqFreq / MAIN_LOOP_FREQUENCY; logFreq = logFreq / MAIN_LOOP_FREQUENCY; boolean nsExists = false; long counter = 0; while(stop == false) { try { if(counter % acqFreq == 0) // Time to get statistics from the channels { nsExists = eventModel.getChannelStatistics(); if(false == nsExists) { setServicesStatus(Status.UNKNOWN); if(eventModel.reconnectNamingService() == false) { logger.log(AcsLogLevel.ERROR,"Naming Service is unreachable"); } else { nsExists = true; } // Calculate statistics } else { // Get notify services NotifyServices ns = eventModel.getNotifyServicesRoot(); // Get services to be treated List<NotifyServiceData> services = getCurrentServices(); // Get channels to be treated List<ChannelData> channels = getCurrentChannels(ns); // Update status of services and channels updateStatus(services, channels); } } // It's time to log statistics if(true == nsExists && (counter+1) % logFreq == 0) { // Log services and channels statistics logStatus(); // After logging statistics, we have to reset them resetStats(); } } catch(Exception e) { logger.warning("Notification Service doesn't exist!"); String str = ""; StackTraceElement [] stack = e.getStackTrace(); for(int k = 0;k < stack.length;++k) { str += stack[k] + "\n"; } logger.warning(e.getMessage() + "\n" + str); } try { ++counter; sleep(MAIN_LOOP_FREQUENCY); } catch (InterruptedException e) { e.printStackTrace(); } } logger.info("nsStatisticsService thread has been finished"); } /** * Stop the execution */ public void stopIt() { this.stop = true; } /* protected String getListString(String [] list) { String str = ""; if(list != null) { for(int i = 0;i < (list.length - 1);++i) { str += list[i] + ", "; } if(list.length > 0) { str += list[list.length-1]; } } return str; } protected String getListStringDiffLines(String prefixLine,String [] list) { String str = ""; if(list != null) { for(int i = 0;i < list.length;++i) { str += prefixLine + list[i] + "\n"; } } return str; } protected String strArray(ArrayList<Integer> list) { String str = ""; if(list != null) { for(int i = 0;i < (list.size() - 1);++i) { str += String.valueOf(list.get(i)) + ", "; } if(list.size() > 0) { str += list.get(list.size()-1); } } return str; } protected int maxArray(ArrayList<Integer> list) { int maxVal = -1; for(Iterator<Integer> it = list.iterator();it.hasNext();) { Integer item = it.next(); if(item.intValue() > maxVal) { maxVal = item.intValue(); } } return maxVal; } */ protected void resetStats() { Iterator services = status.entrySet().iterator(); while(services.hasNext()) { Map.Entry<String,ServiceInfo> entry = (Map.Entry<String,ServiceInfo>)services.next(); Iterator channels = entry.getValue().channels.entrySet().iterator(); while(channels.hasNext()) { Map.Entry<String,ChannelInfo> chEntry = (Map.Entry<String,ChannelInfo>)channels.next(); chEntry.getValue().stats.reset(); } } } public static void logErrors(List<String> errors,boolean sysOutIt,Logger logger) { for(Iterator<String> it = errors.iterator();it.hasNext();) { String msgError = it.next(); if(logger != null) { logger.log(AcsLogLevel.ERROR, msgError); } if(true == sysOutIt) { System.out.println(msgError); } } } public static void main(String[] args) { ServiceParameters params = new ServiceParameters(); List<String> errors = new ArrayList<String>(); boolean exec = params.read(TOOL_NAME,args,errors); if(exec) { try { final EventModel eventModel = EventModel.getInstance(); Logger logger = eventModel.getLogger(); params.log(logger); final nsStatisticsService service = new nsStatisticsService(eventModel,params); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { System.out.println("interrupt received, killing service ..."); service.stopIt(); try { //service.interrupt(); service.join(); } catch (InterruptedException e) { } } }); service.start(); logger.info("Service started ..."); } catch(Throwable thr) { logErrors(errors,true,null); thr.printStackTrace(); } } else { logErrors(errors,true,null); } } }