/* * 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.util; import password.pwm.util.java.JavaHelper; import password.pwm.util.java.TimeDuration; import java.util.concurrent.TimeUnit; public class TransactionSizeCalculator { private final Settings settings; private volatile int transactionSize; private volatile long lastDuration = 1; public TransactionSizeCalculator(final Settings settings) { this.settings = settings; reset(); } public void reset() { transactionSize = settings.getMinTransactions(); lastDuration = settings.getDurationGoal().getTotalMilliseconds(); } public void recordLastTransactionDuration(final long duration) { recordLastTransactionDuration(new TimeDuration(duration)); } public void pause() { JavaHelper.pause(Math.min(lastDuration,settings.getDurationGoal().getTotalMilliseconds() * 2)); } public void recordLastTransactionDuration(final TimeDuration duration) { lastDuration = duration.getTotalMilliseconds(); final long durationGoalMs = settings.getDurationGoal().getTotalMilliseconds(); final long difference = Math.abs(duration.getTotalMilliseconds() - durationGoalMs); final int closeThreshold = (int)(durationGoalMs * .15f); int newTransactionSize; if (duration.isShorterThan(settings.getDurationGoal())) { if (difference > closeThreshold) { newTransactionSize = ((int) (transactionSize + (transactionSize * 0.1)) + 1); } else { newTransactionSize = transactionSize + 1; } } else if (duration.isLongerThan(settings.getDurationGoal())) { if (difference > (10 * durationGoalMs)) { newTransactionSize = settings.getMinTransactions(); } else if (difference > (2 * durationGoalMs)) { newTransactionSize = transactionSize / 2; } else if (difference > closeThreshold) { newTransactionSize = ((int) (transactionSize - (transactionSize * 0.1)) - 1); } else { newTransactionSize = transactionSize - 1; } } else { newTransactionSize = transactionSize; } newTransactionSize = Math.min(newTransactionSize, settings.getMaxTransactions()); newTransactionSize = Math.max(newTransactionSize, settings.getMinTransactions()); this.transactionSize = newTransactionSize; } public int getTransactionSize() { return transactionSize; } public static class Settings { private final TimeDuration durationGoal; private final int maxTransactions; private final int minTransactions; private Settings(final TimeDuration durationGoal, final int maxTransactions, final int minTransactions) { this.durationGoal = durationGoal; this.maxTransactions = maxTransactions; this.minTransactions = minTransactions; if (minTransactions < 1) { throw new IllegalArgumentException("minTransactions must be a positive integer"); } if (maxTransactions < 1) { throw new IllegalArgumentException("maxTransactions must be a positive integer"); } if (minTransactions > maxTransactions) { throw new IllegalArgumentException("minTransactions must be less than maxTransactions"); } if (durationGoal == null) { throw new IllegalArgumentException("durationGoal must not be null"); } if (durationGoal.getTotalMilliseconds() < 1) { throw new IllegalArgumentException("durationGoal must be greater than 0ms"); } } public TimeDuration getDurationGoal() { return durationGoal; } public int getMaxTransactions() { return maxTransactions; } public int getMinTransactions() { return minTransactions; } } public static class SettingsBuilder { private TimeDuration durationGoal = new TimeDuration(100, TimeUnit.MILLISECONDS); private int maxTransactions = 5003; private int minTransactions = 3; public SettingsBuilder setDurationGoal(final TimeDuration durationGoal) { this.durationGoal = durationGoal; return this; } public SettingsBuilder setMaxTransactions(final int maxTransactions) { this.maxTransactions = maxTransactions; return this; } public SettingsBuilder setMinTransactions(final int minTransactions) { this.minTransactions = minTransactions; return this; } public Settings createSettings() { return new Settings(durationGoal, maxTransactions, minTransactions); } } }