/* * Password Management Servlets (PWM) * http://www.pwm-project.org * * Copyright (c) 2006-2009 Novell, Inc. * Copyright (c) 2009-2017 The PWM Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package password.pwm.svc.stats; import password.pwm.util.java.JsonUtil; import password.pwm.util.java.StringUtil; import password.pwm.util.logging.PwmLogger; import java.io.Serializable; import java.math.BigInteger; import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.Map; import java.util.TimeZone; public class StatisticsBundle { private static final PwmLogger LOGGER = PwmLogger.forClass(StatisticsBundle.class); static final SimpleDateFormat STORED_DATETIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); static { STORED_DATETIME_FORMATTER.setTimeZone(TimeZone.getTimeZone("Zulu")); } private final Map<Statistic, String> valueMap = new HashMap<>(); public StatisticsBundle() { } public String output() { return JsonUtil.serializeMap(valueMap); } public static StatisticsBundle input(final String inputString) { final Map<Statistic, String> srcMap = new HashMap<>(); final Map<String, String> loadedMap = JsonUtil.deserializeStringMap(inputString); for (final String key : loadedMap.keySet()) { try { srcMap.put(Statistic.valueOf(key),loadedMap.get(key)); } catch (IllegalArgumentException e) { LOGGER.error("error parsing statistic key '" + key + "', reason: " + e.getMessage()); } } final StatisticsBundle bundle = new StatisticsBundle(); for (final Statistic loopStat : Statistic.values()) { final String value = srcMap.get(loopStat); if (!StringUtil.isEmpty(value)) { bundle.valueMap.put(loopStat, value); } } return bundle; } public synchronized void incrementValue(final Statistic statistic) { if (Statistic.Type.INCREMENTOR != statistic.getType()) { LOGGER.error("attempt to increment non-counter/incremental stat " + statistic); return; } BigInteger currentValue = BigInteger.ZERO; try { if (valueMap.containsKey(statistic)) { currentValue = new BigInteger(valueMap.get(statistic)); } else { currentValue = BigInteger.ZERO; } } catch (NumberFormatException e) { LOGGER.error("error reading counter/incremental stat " + statistic); } final BigInteger newValue = currentValue.add(BigInteger.ONE); valueMap.put(statistic, newValue.toString()); } public synchronized void updateAverageValue(final Statistic statistic, final long timeDuration) { if (Statistic.Type.AVERAGE != statistic.getType()) { LOGGER.error("attempt to update average value of non-average stat " + statistic); return; } final String avgStrValue = valueMap.get(statistic); AverageBean avgBean = new AverageBean(); if (avgStrValue != null && avgStrValue.length() > 0) { try { avgBean = JsonUtil.deserialize(avgStrValue, AverageBean.class); } catch (Exception e) { LOGGER.trace("unable to parse statistics value for stat " + statistic.toString() + ", value=" + avgStrValue); } } avgBean.appendValue(timeDuration); valueMap.put(statistic, JsonUtil.serialize(avgBean)); } public String getStatistic(final Statistic statistic) { switch (statistic.getType()) { case INCREMENTOR: return valueMap.containsKey(statistic) ? valueMap.get(statistic) : "0"; case AVERAGE: final String avgStrValue = valueMap.get(statistic); AverageBean avgBean = new AverageBean(); if (avgStrValue != null && avgStrValue.length() > 0) { try { avgBean = JsonUtil.deserialize(avgStrValue, AverageBean.class); } catch (Exception e) { LOGGER.trace("unable to parse statistics value for stat " + statistic.toString() + ", value=" + avgStrValue); } } return avgBean.getAverage().toString(); default: return ""; } } private static class AverageBean implements Serializable { BigInteger total = BigInteger.ZERO; BigInteger count = BigInteger.ZERO; AverageBean() { } BigInteger getAverage() { if (BigInteger.ZERO.equals(count)) { return BigInteger.ZERO; } return total.divide(count); } void appendValue(final long value) { count = count.add(BigInteger.ONE); total = total.add(BigInteger.valueOf(value)); } } }