/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.jmeter.threads;
import java.io.Serializable;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.apache.jmeter.control.Controller;
import org.apache.jmeter.control.LoopController;
import org.apache.jmeter.engine.event.LoopIterationListener;
import org.apache.jmeter.samplers.RemoteSampleListener;
import org.apache.jmeter.samplers.SampleEvent;
import org.apache.jmeter.samplers.SampleListener;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.testelement.AbstractTestElement;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.property.IntegerProperty;
import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jmeter.testelement.property.LongProperty;
import org.apache.jmeter.testelement.property.BooleanProperty;
import org.apache.jmeter.testelement.property.TestElementProperty;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
/**
* ThreadGroup
*
*/
public class ThreadGroup extends AbstractTestElement implements SampleListener, Serializable, Controller {
private final static Logger log = LoggingManager.getLoggerForClass();
private static final long serialVersionUID = 232L;
public final static String NUM_THREADS = "ThreadGroup.num_threads";
public final static String RAMP_TIME = "ThreadGroup.ramp_time";
public final static String MAIN_CONTROLLER = "ThreadGroup.main_controller";
public final static String SCHEDULER = "ThreadGroup.scheduler";
public final static String START_TIME = "ThreadGroup.start_time";
public final static String END_TIME = "ThreadGroup.end_time";
public final static String DURATION = "ThreadGroup.duration";
public final static String DELAY = "ThreadGroup.delay";
/* Action to be taken when a Sampler error occurs */
public final static String ON_SAMPLE_ERROR = "ThreadGroup.on_sample_error"; // int
public final static String ON_SAMPLE_ERROR_CONTINUE = "continue";
public final static String ON_SAMPLE_ERROR_STOPTHREAD = "stopthread";
public final static String ON_SAMPLE_ERROR_STOPTEST = "stoptest";
private final static int DEFAULT_NUM_THREADS = 1;
private final static int DEFAULT_RAMP_UP = 0;
private SampleQueue queue = null;
private LinkedList listeners = new LinkedList();
private LinkedList remoteListeners = new LinkedList();
private int numberOfThreads = 0; // Number of threads currently running
// in this group
/**
* No-arg constructor.
*/
public ThreadGroup() {
}
/**
* Set the number of threads to start
*
* @param numThreads
* the number of threads.
*/
public void setNumThreads(int numThreads) {
setProperty(new IntegerProperty(NUM_THREADS, numThreads));
}
synchronized void incrNumberOfThreads() {
numberOfThreads++;
}
synchronized void decrNumberOfThreads() {
numberOfThreads--;
}
public synchronized int getNumberOfThreads() {
return numberOfThreads;
}
public boolean isDone() {
return getSamplerController().isDone();
}
public Sampler next() {
return getSamplerController().next();
}
/**
* Set the Scheduler value.
*
* @param Scheduler
* the Scheduler value.
*/
public void setScheduler(boolean Scheduler) {
setProperty(new BooleanProperty(SCHEDULER, Scheduler));
}
/**
* Get the Scheduler value.
*
* @return the Scheduler value.
*/
public boolean getScheduler() {
return getPropertyAsBoolean(SCHEDULER);
}
/**
* Set the StartTime value.
*
* @param stime -
* the StartTime value.
*/
public void setStartTime(long stime) {
setProperty(new LongProperty(START_TIME, stime));
}
/**
* Get the start time value.
*
* @return the start time value.
*/
public long getStartTime() {
return getPropertyAsLong(START_TIME);
}
/**
* Get the duration
*
* @return the duration (in secs)
*/
public long getDuration() {
return getPropertyAsLong(DURATION);
}
/**
* Set the duration
*
* @param duration
* in seconds
*/
public void setDuration(long duration) {
setProperty(new LongProperty(DURATION, duration));
}
/**
* Get the delay
*
* @return the delay (in secs)
*/
public long getDelay() {
return getPropertyAsLong(DELAY);
}
/**
* Set the delay
*
* @param delay
* in seconds
*/
public void setDelay(long delay) {
setProperty(new LongProperty(DELAY, delay));
}
/**
* Set the EndTime value.
*
* @param etime -
* the EndTime value.
*/
public void setEndTime(long etime) {
setProperty(new LongProperty(END_TIME, etime));
}
/**
* Get the end time value.
*
* @return the end time value.
*/
public long getEndTime() {
return getPropertyAsLong(END_TIME);
}
/**
* Set the ramp-up value.
*
* @param rampUp
* the ramp-up value.
*/
public void setRampUp(int rampUp) {
setProperty(new IntegerProperty(RAMP_TIME, rampUp));
}
/**
* Get the ramp-up value.
*
* @return the ramp-up value.
*/
public int getRampUp() {
return getPropertyAsInt(ThreadGroup.RAMP_TIME);
}
/**
* Get the sampler controller.
*
* @return the sampler controller.
*/
public Controller getSamplerController() {
Controller c = (Controller) getProperty(MAIN_CONTROLLER).getObjectValue();
return c;
}
/**
* Set the sampler controller.
*
* @param c
* the sampler controller.
*/
public void setSamplerController(LoopController c) {
c.setContinueForever(false);
setProperty(new TestElementProperty(MAIN_CONTROLLER, c));
}
/**
* Get the number of threads.
*
* @return the number of threads.
*/
public int getNumThreads() {
return this.getPropertyAsInt(ThreadGroup.NUM_THREADS);
}
/**
* Get the default number of threads.
*
* @return the default number of threads.
*/
public int getDefaultNumThreads() {
return DEFAULT_NUM_THREADS;
}
/**
* Get the default ramp-up value.
*
* @return the default ramp-up value (in seconds).
*/
public int getDefaultRampUp() {
return DEFAULT_RAMP_UP;
}
/**
* Add a test element.
*
* @param child
* the test element to add.
*/
public void addTestElement(TestElement child) {
getSamplerController().addTestElement(child);
}
/**
* A sample has occurred.
*
* @param e
* the sample event.
*/
public void sampleOccurred(SampleEvent e) {
if (queue == null) {
queue = new SampleQueue();
Thread thread = new Thread(queue);
// thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
queue.sampleOccurred(e);
}
/**
* A sample has started.
*
* @param e
* the sample event.
*/
public void sampleStarted(SampleEvent e) {
}
/**
* A sample has stopped.
*
* @param e
* the sample event
*/
public void sampleStopped(SampleEvent e) {
}
/**
* Separate thread to deliver all SampleEvents. This ensures that sample
* listeners will get sample events one at a time and can thus ignore thread
* issues.
*
* @author Mike Stover
* @version $Id: ThreadGroup.java 603780 2007-12-13 00:07:06Z sebb $
*/
private class SampleQueue implements Runnable, Serializable {
List occurredQ = Collections.synchronizedList(new LinkedList());
/**
* No-arg constructor.
*/
public SampleQueue() {
}
/**
* A sample occurred.
*
* @param e
* the sample event.
*/
public synchronized void sampleOccurred(SampleEvent e) {
occurredQ.add(e);
this.notifyAll();
}
/**
* Run the thread.
*
* @see java.lang.Runnable#run()
*/
public void run() {
SampleEvent event = null;
while (true) {
try {
event = (SampleEvent) occurredQ.remove(0);
} catch (Exception ex) {
waitForSamples();
continue;
}
try {
if (event != null) {
Iterator iter = listeners.iterator();
while (iter.hasNext()) {
((SampleListener) iter.next()).sampleOccurred(event);
}
iter = remoteListeners.iterator();
while (iter.hasNext()) {
try {
((RemoteSampleListener) iter.next()).sampleOccurred(event);
} catch (Exception ex) {
log.error("", ex);
}
}
} else {
waitForSamples();
}
} catch (Throwable ex) {
log.error("", ex);
}
}
}
private synchronized void waitForSamples() {
try {
this.wait();
} catch (Exception ex) {
log.error("", ex);
}
}
}
/*
* (non-Javadoc)
*
* @see Controller#addIterationListener(LoopIterationListener)
*/
public void addIterationListener(LoopIterationListener lis) {
getSamplerController().addIterationListener(lis);
}
/*
* (non-Javadoc)
*
* @see Controller#initialize()
*/
public void initialize() {
Controller c = getSamplerController();
JMeterProperty property = c.getProperty(TestElement.NAME);
property.setObjectValue(getName()); // Copy our name into that of the controller
property.setRunningVersion(property.isRunningVersion());// otherwise name reverts
c.initialize();
}
/**
* Check if a sampler error should cause thread to stop.
*
* @return true if should stop
*/
public boolean getOnErrorStopThread() {
return getPropertyAsString(ThreadGroup.ON_SAMPLE_ERROR).equalsIgnoreCase(ON_SAMPLE_ERROR_STOPTHREAD);
}
/**
* Check if a sampler error should cause test to stop.
*
* @return true if should stop
*/
public boolean getOnErrorStopTest() {
return getPropertyAsString(ThreadGroup.ON_SAMPLE_ERROR).equalsIgnoreCase(ON_SAMPLE_ERROR_STOPTEST);
}
}