/* * TeleStax, Open Source Cloud Communications Copyright 2012. * and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This 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 software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.mobicents.protocols.ss7.oam.common.statistics; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import javolution.text.TextBuilder; import javolution.util.FastMap; import javolution.xml.XMLBinding; import javolution.xml.XMLObjectReader; import javolution.xml.XMLObjectWriter; import javolution.xml.stream.XMLStreamException; import org.apache.log4j.Logger; import org.mobicents.protocols.ss7.oam.common.jmx.MBeanHost; import org.mobicents.protocols.ss7.oam.common.jmx.MBeanType; import org.mobicents.protocols.ss7.oam.common.statistics.api.ComplexValue; import org.mobicents.protocols.ss7.oam.common.statistics.api.CounterCampaign; import org.mobicents.protocols.ss7.oam.common.statistics.api.CounterDef; import org.mobicents.protocols.ss7.oam.common.statistics.api.CounterDefSet; import org.mobicents.protocols.ss7.oam.common.statistics.api.CounterMediator; import org.mobicents.protocols.ss7.oam.common.statistics.api.CounterOutputFormat; import org.mobicents.protocols.ss7.oam.common.statistics.api.CounterType; import org.mobicents.protocols.ss7.oam.common.statistics.api.CounterValueSet; import org.mobicents.protocols.ss7.oam.common.statistics.api.SourceValueCounter; import org.mobicents.protocols.ss7.oam.common.statistics.api.SourceValueObject; import org.mobicents.protocols.ss7.oam.common.statistics.api.SourceValueSet; /** * * @author sergey vetyutnev * */ public class CounterProviderManagement implements CounterProviderManagementMBean { protected final Logger logger; protected static final String COUNTER_PROVIDER_PERSIST_DIR_KEY = "counterprovider.persist.dir"; protected static final String USER_DIR_KEY = "user.dir"; protected static final String PERSIST_FILE_NAME = "CounterProvider.xml"; private static final String CLASS_ATTRIBUTE = "type"; private static final String TAB_INDENT = "\t"; private static final String COUNTER_CAMPAIGNS = "counterCampaigns"; private static final String COUNTER_CAMPAIGN = "counterCampaign"; private static final XMLBinding binding = new XMLBinding(); private final MBeanHost beanHost; private String name = "CounterHost"; protected final TextBuilder persistFile = TextBuilder.newInstance(); protected String persistDir = null; private boolean isStarted; private int minuteProcessed; private int secondProcessed; private final StatsPrinter statsPrinter; private final CsvStatsPrinter csvStatsPrinter; /** * A list of registered CounterMediator by there names */ protected FastMap<String, CounterMediator> lstCounterMediator = new FastMap<String, CounterMediator>(); /** * A list of registered CounterDefSet by names - values are corresponded CounterMediator's */ private FastMap<String, CounterMediator> lstCounterDefSet = new FastMap<String, CounterMediator>(); protected CounterCampaignMap<String, CounterCampaignImpl> lstCounterCampaign = new CounterCampaignMap<String, CounterCampaignImpl>(); public CounterProviderManagement(MBeanHost beanHost) { this.beanHost = beanHost; binding.setClassAttribute(CLASS_ATTRIBUTE); binding.setAlias(CounterCampaignImpl.class, COUNTER_CAMPAIGN); binding.setAlias(String.class, "String"); this.logger = Logger.getLogger(CounterProviderManagement.class.getCanonicalName() + "-" + this.name); this.statsPrinter = new StatsPrinter(); this.csvStatsPrinter = new CsvStatsPrinter(); } @Override public String getName() { return name; } public void setName(String val) { this.name = val; } public String getPersistDir() { return persistDir; } public void setPersistDir(String persistDir) { this.persistDir = persistDir; } public void start() { logger.info("Starting ..."); this.persistFile.clear(); if (persistDir != null) { this.persistFile.append(persistDir).append(File.separator).append(this.name).append("_").append(PERSIST_FILE_NAME); } else { persistFile.append(System.getProperty(COUNTER_PROVIDER_PERSIST_DIR_KEY, System.getProperty(USER_DIR_KEY))) .append(File.separator).append(this.name).append("_").append(PERSIST_FILE_NAME); } logger.info(String.format("CounterManagement configuration file path %s", persistFile.toString())); this.lstCounterCampaign.clear(); this.load(); synchronized (this) { isStarted = true; lstCounterMediator.clear(); lstCounterDefSet.clear(); this.beanHost.registerMBean(CounterLayer.COUNTER, CounterManagementType.MANAGEMENT, this.name, this); secondProcessed = -1; minuteProcessed = -1; Thread t1 = new Thread(new CouterProcessingClass(true)); t1.start(); Thread t2 = new Thread(new CouterProcessingClass(false)); t2.start(); } logger.info("Started ..."); } public void stop() { logger.info("Stopping ..."); isStarted = false; logger.info("Stopped ..."); } public void registerCounterMediator(CounterMediator val) { synchronized (this) { lstCounterMediator.put(val.getCounterMediatorName(), val); String[] ss = val.getCounterDefSetList(); if (ss != null) { for (String s : ss) { lstCounterDefSet.put(s, val); for (CounterCampaignImpl cc : this.lstCounterCampaign.values()) { if (cc.getCounterSetName().equals(s)) { cc.setCounterSet(val.getCounterDefSet(s)); } } } } logger.info("Registered CounterMediator: " + val.getCounterMediatorName()); } } public void unRegisterCounterMediator(CounterMediator val) { synchronized (this) { lstCounterMediator.remove(val.getCounterMediatorName()); ArrayList<String> toDel = new ArrayList<String>(); for (String s : lstCounterDefSet.keySet()) { if (lstCounterDefSet.get(s).getCounterMediatorName().equals(val.getCounterMediatorName())) { toDel.add(s); } } for (String s : toDel) { lstCounterDefSet.remove(s); } logger.info("Unregistered CounterMediator: " + val.getCounterMediatorName()); } } @Override public String[] getCounterDefSetList() { String[] ress = new String[lstCounterDefSet.size()]; int i1 = 0; synchronized (this) { for (String s : lstCounterDefSet.keySet()) { ress[i1++] = s; } } return ress; } @Override public CounterDefSet getCounterDefSet(String counterDefSetName) { synchronized (this) { CounterMediator cm = lstCounterDefSet.get(counterDefSetName); if (cm != null) { return cm.getCounterDefSet(counterDefSetName); } } return null; } @Override public void createCampaign(String campaignName, String counterSetName, int duration, int outputFormat) throws Exception { this.doCreateCampaign(campaignName, counterSetName, duration, false, outputFormat); } @Override public void createShortCampaign(String campaignName, String counterSetName, int duration, int outputFormat) throws Exception { this.doCreateCampaign(campaignName, counterSetName, duration, true, outputFormat); } private void doCreateCampaign(String campaignName, String counterSetName, int duration, boolean shortCampaign, int outputFormat) throws Exception { if (campaignName == null) { throw new Exception("Campaign Name cannot be null"); } if (counterSetName == null) { throw new Exception("CounterSet Name cannot be null"); } if (lstCounterCampaign.containsKey(campaignName)) throw new Exception("Campaign " + campaignName + " already exists"); if (duration != 5 && duration != 10 && duration != 15 && duration != 20 && duration != 30 && duration != 60) throw new Exception("Duration may be only 5, 10, 15, 20, 30 or 60 minutes/seconds"); if (outputFormat != 0 && outputFormat != 1 && outputFormat != 2) throw new Exception("Output format may be only CSV, verbose or CSV and verbose"); CounterOutputFormat counterOutputFormat = CounterOutputFormat.getInstance(outputFormat); CounterMediator cm = lstCounterDefSet.get(counterSetName); if (cm == null) { throw new Exception("CounterMediator is null for counterSetName=" + counterSetName); } synchronized (this) { CounterDefSet counterSet = cm.getCounterDefSet(counterSetName); CounterCampaignImpl camp = new CounterCampaignImpl(campaignName, counterSetName, counterSet, duration, shortCampaign, counterOutputFormat); lstCounterCampaign.put(campaignName, camp); this.store(); } if (logger.isInfoEnabled()) { logger.info("Created campaign: name=" + campaignName + ", counterSetName=" + counterSetName + ", duration=" + duration); } } @Override public void destroyCampaign(String campaignName) throws Exception { if (!lstCounterCampaign.containsKey(campaignName)) throw new Exception("Campaign " + campaignName + " not found"); synchronized (this) { lstCounterCampaign.remove(campaignName); this.store(); } if (logger.isInfoEnabled()) { logger.info("Campaign destroyed: name=" + campaignName); } } @Override public String[] getCampaignsList() { synchronized (this) { String[] res = new String[lstCounterCampaign.size()]; int i1 = 0; for (String s : lstCounterCampaign.keySet()) { res[i1++] = s; } return res; } } @Override public CounterCampaign getCampaign(String campaignName) { synchronized (this) { return lstCounterCampaign.get(campaignName); } } @Override public CounterValueSet getLastCounterValues(String campaignName) { synchronized (this) { CounterCampaign camp = lstCounterCampaign.get(campaignName); if (camp != null) return camp.getLastCounterValueSet(); return null; } } protected void processCampaign(CounterCampaignImpl cc, Date endTime) { if (cc.isShortCampaign()) logger.debug("Campaign processing: name=" + cc.getName()); else logger.info("Campaign processing: name=" + cc.getName()); try { CounterMediator cm = null; synchronized (this) { cm = lstCounterDefSet.get(cc.getCounterSetName()); } if (cm != null) { SourceValueSet svs1 = cc.getLastSourceValueSet(); CounterOutputFormat outputFormat = cc.getOutputFormat(); if (outputFormat != null) { switch (outputFormat) { case CSV: this.csvStatsPrinter.printCsvStats(cc); break; case VERBOSE: this.statsPrinter.printStats(cc); break; case CSV_AND_VERBOSE: this.statsPrinter.printStats(cc); this.csvStatsPrinter.printCsvStats(cc); break; } } else { logger.info("Output format not set for campaign: " + cc.getName() + ", using default"); this.statsPrinter.printStats(cc); } int durationInSeconds = cc.getDuration(); if (!cc.isShortCampaign()) { durationInSeconds *= 60; } SourceValueSet svs2 = cm.getSourceValueSet(cc.getCounterSetName(), cc.getName(), durationInSeconds); if (logger.isDebugEnabled()) { logger.debug("svs1 = " + svs1 + " svs2 = " + svs2); } cc.setLastSourceValueSet(svs2); cc.setCounterValueSet(null); if (svs1 == null || svs2 == null || !svs1.getSessionId().equals(svs2.getSessionId())) { return; } Date startTime; int duration; int durationSeconds; if (cc.isShortCampaign()) { startTime = new Date(endTime.getTime() - cc.getDuration() * 1000); duration = cc.getDuration(); durationSeconds = cc.getDuration() * 60; } else { startTime = new Date(endTime.getTime() - cc.getDuration() * 60 * 1000); duration = cc.getDuration(); durationSeconds = 0; } CounterValueSetImpl res = new CounterValueSetImpl(startTime, endTime, duration, durationSeconds); for (SourceValueCounter sv2 : svs2.getCounters().values()) { CounterDef cd2 = sv2.getCounterDef(); SourceValueCounter sv1 = svs1.getCounters().get(sv2.getCounterDef().getCounterName()); if (sv1 == null) continue; CounterDef cd1 = sv1.getCounterDef(); if (cd1.getCounterType() != cd2.getCounterType()) continue; for (SourceValueObject obj2 : sv2.getObjects().values()) { SourceValueObject obj1 = sv1.getObjects().get(obj2.getObjectName()); if (obj1 == null && (cd2.getCounterType() != CounterType.Minimal && cd2.getCounterType() != CounterType.Maximal && cd2 .getCounterType() != CounterType.ComplexValue)) continue; CounterValueImpl val = null; switch (cd2.getCounterType()) { case Summary_Cumulative: long valr = obj2.getValue(); val = new CounterValueImpl(cd2, obj2.getObjectName(), valr); break; case Summary: valr = obj2.getValue() - obj1.getValue(); val = new CounterValueImpl(cd2, obj2.getObjectName(), valr); break; case SummaryDouble: double vald = obj2.getValueA() - obj1.getValueA(); val = new CounterValueImpl(cd2, obj2.getObjectName(), 0); val.setDoubleValue(vald); break; case Minimal: val = new CounterValueImpl(cd2, obj2.getObjectName(), obj2.getValue()); break; case Maximal: val = new CounterValueImpl(cd2, obj2.getObjectName(), obj2.getValue()); break; case Average: double d1 = obj2.getValueA() - obj1.getValueA(); double d2 = obj2.getValueB() - obj1.getValueB(); val = new CounterValueImpl(cd2, obj2.getObjectName(), 0); if (d2 != 0) { val.setDoubleValue(d1 / d2); } break; case ComplexValue: val = new CounterValueImpl(cd2, obj2.getObjectName(), 0); for (ComplexValue cv : obj2.getComplexValue()) { val.addComplexValue(cv); } break; } if (val != null) res.addCounterValue(val); } } cc.setCounterValueSet(res); } else { logger.error("CounterMediator not found for " + cc.getCounterSetName()); } } catch (Throwable e) { logger.info("Exception when campaign processing: name=" + cc.getName() + " - " + e.getMessage(), e); } if (cc.isShortCampaign()) logger.debug("Campaign processed: name=" + cc.getName()); else logger.info("Campaign processed: name=" + cc.getName()); } /** * Persist */ public void store() { try { XMLObjectWriter writer = XMLObjectWriter.newInstance(new FileOutputStream(persistFile.toString())); writer.setBinding(binding); writer.setIndentation(TAB_INDENT); writer.write(this.lstCounterCampaign, COUNTER_CAMPAIGNS, CounterCampaignMap.class); writer.close(); } catch (Exception e) { logger.error("Error while persisting the CounterProvider state in file", e); } } /** * Load and create LinkSets and Link from persisted file * * @throws Exception */ private void load() { try { File f = new File(persistFile.toString()); if (f.exists()) { XMLObjectReader reader = XMLObjectReader.newInstance(new FileInputStream(persistFile.toString())); reader.setBinding(binding); this.lstCounterCampaign = reader.read(COUNTER_CAMPAIGNS, CounterCampaignMap.class); reader.close(); } } catch (XMLStreamException ex) { logger.error(String.format("Failed to load the CounterProvider configuration file. \n%s", ex.getMessage())); } catch (FileNotFoundException e) { logger.warn(String.format("Failed to load the CounterProvider configuration file. \n%s", e.getMessage())); } catch (IOException e) { logger.error(String.format("Failed to load the CounterProvider configuration file. \n%s", e.getMessage())); } } private class CouterProcessingClass implements Runnable { private boolean shortCampaign; public CouterProcessingClass(boolean shortCampaign) { this.shortCampaign = shortCampaign; } @Override public void run() { while (true) { if (!isStarted) return; try { Thread.sleep(shortCampaign ? 200 : 1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // firstly we check if it a begin of minute 0-5-10-15-20-25-30-35-40-45-50-55 Date dt = new Date(); Date endTime; int minuteSecond; if (shortCampaign) { int sc = dt.getSeconds(); if (sc != 0 && sc != 5 && sc != 10 && sc != 15 && sc != 20 && sc != 25 && sc != 30 && sc != 35 && sc != 40 && sc != 45 && sc != 50 && sc != 55) continue; if (sc == secondProcessed) continue; secondProcessed = sc; minuteSecond = sc; endTime = new Date(dt.getYear(), dt.getMonth(), dt.getDate(), dt.getHours(), dt.getMinutes(), dt.getSeconds()); } else { if (dt.getSeconds() > 30) continue; int mn = dt.getMinutes(); if (mn != 0 && mn != 5 && mn != 10 && mn != 15 && mn != 20 && mn != 25 && mn != 30 && mn != 35 && mn != 40 && mn != 45 && mn != 50 && mn != 55) continue; if (mn == minuteProcessed) continue; minuteProcessed = mn; minuteSecond = mn; endTime = new Date(dt.getYear(), dt.getMonth(), dt.getDate(), dt.getHours(), dt.getMinutes(), 0); } // campaign list CounterCampaignImpl[] ccc; synchronized (this) { ccc = new CounterCampaignImpl[lstCounterCampaign.size()]; int i1 = 0; for (CounterCampaignImpl cc : lstCounterCampaign.values()) { ccc[i1++] = cc; } } for (CounterCampaignImpl cc : ccc) { if (shortCampaign == cc.isShortCampaign() && minuteSecond % cc.getDuration() == 0) { // we process only needed duration processCampaign(cc, endTime); } } } } } public enum CounterManagementType implements MBeanType { MANAGEMENT("Management"); private final String name; public static final String NAME_MANAGEMENT = "Management"; private CounterManagementType(String name) { this.name = name; } public String getName() { return this.name; } public static CounterManagementType getInstance(String name) { if (NAME_MANAGEMENT.equals(name)) { return MANAGEMENT; } return null; } } }