001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.tool.sampler;
018
019import java.util.concurrent.CountDownLatch;
020import java.util.concurrent.atomic.AtomicBoolean;
021
022import org.apache.activemq.tool.ClientRunBasis;
023import org.apache.activemq.tool.properties.AbstractObjectProperties;
024import org.apache.activemq.tool.reports.PerformanceReportWriter;
025import org.apache.commons.lang.Validate;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029public abstract class AbstractPerformanceSampler extends AbstractObjectProperties implements PerformanceSampler {
030    
031    private final Logger log = LoggerFactory.getLogger(this.getClass());
032
033    protected long rampUpPercent = 0;
034    protected long rampDownPercent = 0;
035
036    // the following are all optionally set; they are otherwise worked out at run time
037    protected Long rampUpTime;
038    protected Long rampDownTime;
039    protected Long duration;
040
041    protected long interval = 1000; // 1 sec
042    protected PerformanceReportWriter perfReportWriter;
043    protected PerformanceEventListener perfEventListener;
044    protected final AtomicBoolean isRunning = new AtomicBoolean(false);
045    protected CountDownLatch completionLatch;
046    protected long sampleIndex;
047
048    @Override
049    public Long getRampUpTime() {
050        return rampUpTime;
051    }
052
053    @Override
054    public void setRampUpTime(long rampUpTime) {
055        this.rampUpTime = rampUpTime;
056    }
057
058    @Override
059    public Long getRampDownTime() {
060        return rampDownTime;
061    }
062
063    @Override
064    public void setRampDownTime(long rampDownTime) {
065        this.rampDownTime = rampDownTime;
066    }
067
068    @Override
069    public Long getDuration() {
070        return duration;
071    }
072
073    @Override
074    public void setDuration(long duration) {
075        this.duration = duration;
076    }
077
078    @Override
079    public long getInterval() {
080        return interval;
081    }
082
083    @Override
084    public void setInterval(long interval) {
085        this.interval = interval;
086    }
087
088    @Override
089    public long getRampUpPercent() {
090        return rampUpPercent;
091    }
092
093    @Override
094    public void setRampUpPercent(long rampUpPercent) {
095        Validate.isTrue((rampUpPercent >= 0) && (rampUpPercent <= 100), "rampUpPercent must be a value between 0 and 100");
096        this.rampUpPercent = rampUpPercent;
097    }
098
099    @Override
100    public long getRampDownPercent() {
101        return rampDownPercent;
102    }
103
104    @Override
105    public void setRampDownPercent(long rampDownPercent) {
106        Validate.isTrue((rampDownPercent >= 0) && (rampDownPercent < 100), "rampDownPercent must be a value between 0 and 99");
107        this.rampDownPercent = rampDownPercent;
108    }
109
110    @Override
111    public PerformanceReportWriter getPerfReportWriter() {
112        return perfReportWriter;
113    }
114
115    @Override
116    public void setPerfReportWriter(PerformanceReportWriter perfReportWriter) {
117        this.perfReportWriter = perfReportWriter;
118    }
119
120    @Override
121    public PerformanceEventListener getPerfEventListener() {
122        return perfEventListener;
123    }
124
125    @Override
126    public void setPerfEventListener(PerformanceEventListener perfEventListener) {
127        this.perfEventListener = perfEventListener;
128    }
129
130    @Override
131    public void startSampler(CountDownLatch completionLatch, ClientRunBasis clientRunBasis, long clientRunDuration) {
132        Validate.notNull(clientRunBasis);
133        Validate.notNull(completionLatch);
134
135        if (clientRunBasis == ClientRunBasis.time) {
136            // override the default durations
137            // if the user has overridden a duration, then use that
138            duration = (duration == null) ? clientRunDuration : this.duration;
139            rampUpTime = (rampUpTime == null) ? (duration / 100 * rampUpPercent) : this.rampUpTime;
140            rampDownTime = (rampDownTime == null) ? (duration / 100 * rampDownPercent) : this.rampDownTime;
141
142            Validate.isTrue(duration >= (rampUpTime + rampDownTime),
143                    "Ramp times (up: " + rampDownTime + ", down: " + rampDownTime + ") exceed the sampler duration (" + duration + ")");
144            log.info("Sampling duration: {} ms, ramp up: {} ms, ramp down: {} ms", duration, rampUpTime, rampDownTime);
145
146            // spawn notifier thread to stop the sampler, taking ramp-down time into account
147            Thread notifier = new Thread(new RampDownNotifier(this));
148            notifier.setName("RampDownNotifier[" + this.getClass().getSimpleName() + "]");
149            notifier.start();
150        } else {
151            log.info("Performance test running on count basis; ignoring duration and ramp times");
152            setRampUpTime(0);
153            setRampDownTime(0);
154        }
155
156        this.completionLatch = completionLatch;
157        Thread t = new Thread(this);
158        t.setName(this.getClass().getSimpleName());
159        t.start();
160        isRunning.set(true);
161    }
162
163    @Override
164    public void finishSampling() {
165        isRunning.set(false);
166    }
167
168    @Override
169    public void run() {
170        try {
171            log.debug("Ramp up start");
172            onRampUpStart();
173            if (perfEventListener != null) {
174                perfEventListener.onRampUpStart(this);
175            }
176
177            if (rampUpTime > 0) {
178                try {
179                    Thread.sleep(rampUpTime);
180                } catch (InterruptedException e) {
181                    e.printStackTrace();
182                }
183            }
184
185            log.debug("Sampler start");
186            onSamplerStart();
187            if (perfEventListener != null) {
188                perfEventListener.onSamplerStart(this);
189            }
190
191            sample();
192
193            log.debug("Sampler end");
194            onSamplerEnd();
195            if (perfEventListener != null) {
196                perfEventListener.onSamplerEnd(this);
197            }
198
199            if (rampDownTime > 0) {
200                try {
201                    Thread.sleep(rampDownTime);
202                } catch (InterruptedException e) {
203                    e.printStackTrace();
204                }
205            }
206
207            log.debug("Ramp down end");
208            onRampDownEnd();
209            if (perfEventListener != null) {
210                perfEventListener.onRampDownEnd(this);
211            }
212        } finally {
213            completionLatch.countDown();
214        }
215    }
216
217    protected void sample() {
218        while (isRunning.get()) {
219            try {
220                Thread.sleep(interval);
221            } catch (InterruptedException e) {
222                e.printStackTrace();
223            }
224            sampleData();
225            sampleIndex++;
226        }
227    }
228
229    @Override
230    public abstract void sampleData();
231
232    // Call back functions to customize behavior of thread.
233    protected void onRampUpStart() {
234    }
235
236    protected void onSamplerStart() {
237    }
238
239    protected void onSamplerEnd() {
240    }
241
242    protected void onRampDownEnd() {
243    }
244}