All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.grinder.console.model.SampleModelImplementation Maven / Gradle / Ivy

The newest version!
// Copyright (C) 2001 - 2012 Philip Aston
// Copyright (C) 2012 Marc Holden
// All rights reserved.
//
// This file is part of The Grinder software distribution. Refer to
// the file LICENSE which is part of The Grinder distribution for
// licensing details. The Grinder distribution is available on the
// Internet at http://grinder.sourceforge.net/
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

package net.grinder.console.model;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeSet;

import net.grinder.common.GrinderException;
import net.grinder.common.Test;
import net.grinder.console.common.ErrorHandler;
import net.grinder.console.common.Resources;
import net.grinder.statistics.PeakStatisticExpression;
import net.grinder.statistics.StatisticExpression;
import net.grinder.statistics.StatisticExpressionFactory;
import net.grinder.statistics.StatisticsIndexMap;
import net.grinder.statistics.StatisticsServices;
import net.grinder.statistics.StatisticsSet;
import net.grinder.statistics.TestStatisticsMap;
import net.grinder.util.ListenerSupport;


/**
 * Collate test reports into samples and distribute to listeners.
 *
 * 

* When notifying listeners of changes to the number of tests we send copies of * the new index arrays. This helps because most listeners are Swing dispatched * and so can't guarantee the model is in a reasonable state when they call * back. *

* * @author Philip Aston */ public final class SampleModelImplementation implements SampleModel { private final ConsoleProperties m_properties; private final StatisticsServices m_statisticsServices; private final Timer m_timer; private final ErrorHandler m_errorHandler; private final String m_stateIgnoringString; private final String m_stateWaitingString; private final String m_stateStoppedString; private final String m_stateCapturingString; private final String m_unknownTestString; /** * The current test set. A TreeSet is used to maintain the test * order. Guarded by itself. */ private final Set m_tests = new TreeSet(); private final ListenerSupport m_listeners = new ListenerSupport(); private final StatisticsIndexMap.LongIndex m_periodIndex; private final StatisticExpression m_tpsExpression; private final PeakStatisticExpression m_peakTPSExpression; private final SampleAccumulator m_totalSampleAccumulator; /** * A {@link SampleAccumulator} for each test. Guarded by itself. */ private final Map m_accumulators = Collections.synchronizedMap(new HashMap()); // Guarded by this. private InternalState m_state; /** * Creates a new SampleModelImplementation instance. * * @param properties The console properties. * @param statisticsServices Statistics services. * @param timer A timer. * @param resources Console resources. * @param errorHandler Error handler * * @exception GrinderException if an error occurs */ public SampleModelImplementation(final ConsoleProperties properties, final StatisticsServices statisticsServices, final Timer timer, final Resources resources, final ErrorHandler errorHandler) throws GrinderException { m_properties = properties; m_statisticsServices = statisticsServices; m_timer = timer; m_errorHandler = errorHandler; m_stateIgnoringString = resources.getString("state.ignoring.label") + ' '; m_stateWaitingString = resources.getString("state.waiting.label"); m_stateStoppedString = resources.getString("state.stopped.label"); m_stateCapturingString = resources.getString("state.capturing.label") + ' '; m_unknownTestString = resources.getString("ignoringUnknownTest.text"); final StatisticsIndexMap indexMap = statisticsServices.getStatisticsIndexMap(); m_periodIndex = indexMap.getLongIndex("period"); final StatisticExpressionFactory statisticExpressionFactory = m_statisticsServices.getStatisticExpressionFactory(); m_tpsExpression = statisticsServices.getTPSExpression(); m_peakTPSExpression = statisticExpressionFactory.createPeak( indexMap.getDoubleIndex("peakTPS"), m_tpsExpression); m_totalSampleAccumulator = new SampleAccumulator(m_peakTPSExpression, m_periodIndex, m_statisticsServices.getStatisticsSetFactory()); setInternalState(new WaitingForTriggerState()); } /** * {@inheritDoc} */ @Override public StatisticExpression getTPSExpression() { return m_tpsExpression; } /** * {@inheritDoc} */ @Override public StatisticExpression getPeakTPSExpression() { return m_peakTPSExpression; } /** * {@inheritDoc} */ @Override public void registerTests(final Collection tests) { // Need to copy collection, might be immutable. final Set newTests = new HashSet(tests); final Test[] testArray; synchronized (m_tests) { newTests.removeAll(m_tests); if (newTests.size() == 0) { // No new tests. return; } m_tests.addAll(newTests); // Create an index of m_tests sorted by test number. testArray = m_tests.toArray(new Test[m_tests.size()]); } final SampleAccumulator[] accumulatorArray = new SampleAccumulator[testArray.length]; synchronized (m_accumulators) { for (final Test test : newTests) { m_accumulators.put(test, new SampleAccumulator( m_peakTPSExpression, m_periodIndex, m_statisticsServices.getStatisticsSetFactory())); } for (int i = 0; i < accumulatorArray.length; i++) { accumulatorArray[i] = m_accumulators.get(testArray[i]); } } final ModelTestIndex modelTestIndex = new ModelTestIndex(testArray, accumulatorArray); m_listeners.apply( new ListenerSupport.Informer() { @Override public void inform(final Listener l) { l.newTests(newTests, modelTestIndex); } }); } /** * {@inheritDoc} */ @Override public StatisticsSet getTotalCumulativeStatistics() { return m_totalSampleAccumulator.getCumulativeStatistics(); } /** * {@inheritDoc} */ @Override public StatisticsSet getTotalLatestStatistics() { return m_totalSampleAccumulator.getLastSampleStatistics(); } /** * {@inheritDoc} */ @Override public void addModelListener(final Listener listener) { m_listeners.add(listener); } /** * {@inheritDoc} */ @Override public void addSampleListener(final Test test, final SampleListener listener) { final SampleAccumulator sampleAccumulator = m_accumulators.get(test); if (sampleAccumulator != null) { sampleAccumulator.addSampleListener(listener); } } /** * {@inheritDoc} */ @Override public void addTotalSampleListener(final SampleListener listener) { m_totalSampleAccumulator.addSampleListener(listener); } /** * {@inheritDoc} */ @Override public void reset() { synchronized (m_tests) { m_tests.clear(); } m_accumulators.clear(); m_totalSampleAccumulator.zero(); m_listeners.apply( new ListenerSupport.Informer() { @Override public void inform(final Listener l) { l.resetTests(); } }); } /** * {@inheritDoc} */ @Override public void zeroStatistics() { zero(); } /** * {@inheritDoc} */ @Override public void start() { getInternalState().start(); } /** * {@inheritDoc} */ @Override public void stop() { getInternalState().stop(); } /** * {@inheritDoc} */ @Override public void addTestReport(final TestStatisticsMap testStatisticsMap) { getInternalState().newTestReport(testStatisticsMap); } /** * {@inheritDoc} */ @Override public State getState() { return getInternalState().toExternalState(); } private void zero() { synchronized (m_accumulators) { for (final SampleAccumulator sampleAccumulator : m_accumulators.values()) { sampleAccumulator.zero(); } } m_totalSampleAccumulator.zero(); } private InternalState getInternalState() { synchronized (this) { return m_state; } } private void setInternalState(final InternalState newState) { synchronized (this) { m_state = newState; } m_listeners.apply( new ListenerSupport.Informer() { @Override public void inform(final Listener l) { l.stateChanged(); } }); } private interface InternalState { State toExternalState(); void start(); void stop(); void newTestReport(TestStatisticsMap testStatisticsMap); } private abstract class AbstractInternalState implements InternalState, State { protected final boolean isActiveState() { return getInternalState() == this; } @Override public final State toExternalState() { // We don't bother cloning the state, only the description varies. return this; } @Override public final void start() { // Valid transition for all states. setInternalState(new WaitingForTriggerState()); } @Override public final void stop() { // Valid transition for all states. setInternalState(new StoppedState()); } @Override public long getSampleCount() { return -1; } } private final class WaitingForTriggerState extends AbstractInternalState { public WaitingForTriggerState() { zero(); } @Override public void newTestReport(final TestStatisticsMap testStatisticsMap) { if (m_properties.getIgnoreSampleCount() == 0) { setInternalState(new CapturingState()); } else { setInternalState(new TriggeredState()); } // Ensure the the first sample is recorded. getInternalState().newTestReport(testStatisticsMap); } @Override public String getDescription() { return m_stateWaitingString; } @Override public Value getValue() { return Value.WaitingForFirstReport; } } private final class StoppedState extends AbstractInternalState { @Override public void newTestReport(final TestStatisticsMap testStatisticsMap) { } @Override public String getDescription() { return m_stateStoppedString; } @Override public Value getValue() { return Value.Stopped; } } private abstract class SamplingState extends AbstractInternalState { // Guarded by this. private long m_lastTime = 0; private volatile long m_sampleCount = 1; @Override public final void newTestReport(final TestStatisticsMap testStatisticsMap) { testStatisticsMap.new ForEach() { @Override public void next(final Test test, final StatisticsSet statistics) { final SampleAccumulator sampleAccumulator = m_accumulators.get(test); if (sampleAccumulator == null) { m_errorHandler.handleInformationMessage( m_unknownTestString + " " + test); } else { sampleAccumulator.addIntervalStatistics(statistics); if (shouldAccumulateSamples()) { sampleAccumulator.addCumulativeStaticstics(statistics); } if (!statistics.isComposite()) { m_totalSampleAccumulator.addIntervalStatistics(statistics); if (shouldAccumulateSamples()) { m_totalSampleAccumulator.addCumulativeStaticstics(statistics); } } } } } .iterate(); } protected final void schedule() { synchronized (this) { if (m_lastTime == 0) { m_lastTime = System.currentTimeMillis(); } } m_timer.schedule( new TimerTask() { @Override public void run() { sample(); } }, m_properties.getSampleInterval()); } public final void sample() { if (!isActiveState()) { return; } try { final long period; synchronized (this) { period = System.currentTimeMillis() - m_lastTime; } final long sampleInterval = m_properties.getSampleInterval(); synchronized (m_accumulators) { for (final SampleAccumulator sampleAccumulator : m_accumulators.values()) { sampleAccumulator.fireSample(sampleInterval, period); } } m_totalSampleAccumulator.fireSample(sampleInterval, period); ++m_sampleCount; // I'm ignoring a minor race here: the model could have been stopped // after the task was started. // We call setInternalState() even if the InternalState hasn't // changed since we've altered the sample count. setInternalState(nextState()); m_listeners.apply( new ListenerSupport.Informer() { @Override public void inform(final Listener l) { l.newSample(); } }); } finally { synchronized (this) { if (isActiveState()) { schedule(); } } } } @Override public final long getSampleCount() { return m_sampleCount; } protected abstract boolean shouldAccumulateSamples(); protected abstract InternalState nextState(); } private final class TriggeredState extends SamplingState { public TriggeredState() { schedule(); } @Override protected boolean shouldAccumulateSamples() { return false; } @Override protected InternalState nextState() { if (getSampleCount() > m_properties.getIgnoreSampleCount()) { return new CapturingState(); } return this; } @Override public String getDescription() { return m_stateIgnoringString + getSampleCount(); } @Override public Value getValue() { return Value.IgnoringInitialSamples; } } private final class CapturingState extends SamplingState { public CapturingState() { zero(); schedule(); } @Override protected boolean shouldAccumulateSamples() { return true; } @Override protected InternalState nextState() { final int collectSampleCount = m_properties.getCollectSampleCount(); if (collectSampleCount != 0 && getSampleCount() > collectSampleCount) { return new StoppedState(); } return this; } @Override public String getDescription() { return m_stateCapturingString + getSampleCount(); } @Override public Value getValue() { return Value.Recording; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy