/**
*
* Copyright 2004 Protique Ltd
*
* Licensed 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.codehaus.activemq.producer.sampler;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.Socket;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.jmeter.config.ConfigTestElement;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.engine.event.LoopIterationEvent;
import org.apache.jmeter.samplers.AbstractSampler;
import org.apache.jmeter.samplers.Entry;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.testelement.TestListener;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import java.text.NumberFormat;
import javax.jms.Session;
import javax.jms.JMSException;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.DeliveryMode;
import javax.jms.Connection;
import javax.jms.Message;
import org.codehaus.activemq.ActiveMQConnectionFactory;
import org.codehaus.activemq.util.IdGenerator;
import EDU.oswego.cs.dl.util.concurrent.SynchronizedInt;
import java.util.List;
import java.util.ArrayList;
/**
* A sampler which understands Tcp requests.
*/
public class ProducerSampler extends AbstractSampler implements TestListener {
private static final Logger log = LoggingManager.getLoggerForClass();
public final static String FILENAME = "ProducerSampler.filename";
public final static String CLASSNAME = "ProducerSampler.classname";
public final static String URL = "ProducerSampler.url";
public final static String DURATION = "ProducerSampler.duration";
public final static String RAMP_UP = "ProducerSampler.ramp_up";
public final static String NOPROD = "ProducerSampler.noprod";
public final static String NOSUBJECT = "ProducerSampler.nosubject";
public final static String DURABLE = "ProducerSampler.durable";
public final static String TOPIC = "ProducerSampler.topic";
public final static String TOOL_DEFAULT = "TOOL.DEFAULT";
public final static String MSGSIZE = "ProducerSampler.msgsize";
private static final int DEFAULT_DURATION = 5;
private static final int DEFAULT_RAMP_UP = 1;
private final static String TCPKEY = "TCP";
private final static String ERRKEY = "ERR";
public final static int TIMEOUT = 1000;
public final static boolean NODELAY = true;
private static Set allSockets = new HashSet();// Keep track of connections to allow close
// Otherwise, the response is scanned for these strings
private final static String STATUS_PREFIX = JMeterUtils.getPropDefault("tcp.status.prefix", "");
private final static String STATUS_SUFFIX = JMeterUtils.getPropDefault("tcp.status.suffix", "");
private final static String STATUS_PROPERTIES = JMeterUtils.getPropDefault("tcp.status.properties", "");
private final static Properties statusProps = new Properties();
private static boolean haveStatusProps = false;
protected String[] subjects;
protected String[] producers;
public static int counter;
public static int duration;
public static int ramp_up;
private long time = System.currentTimeMillis();
private NumberFormat formatter = NumberFormat.getInstance();
private ActiveMQConnectionFactory factory;
int loops = -1;
int loopSize = 1000;
private SynchronizedInt connectionCounter = new SynchronizedInt(0);
private IdGenerator idGenerator = new IdGenerator();
private List resources = new ArrayList();
protected boolean embeddedBroker = false;
static int answer1 = 0;
static {
log.info("Protocol Handler name=" + getClassname());
log.info("Status prefix=" + STATUS_PREFIX);
log.info("Status suffix=" + STATUS_SUFFIX);
log.info("Status properties=" + STATUS_PROPERTIES);
if (STATUS_PROPERTIES.length() > 0) {
File f = new File(STATUS_PROPERTIES);
try {
statusProps.load(new FileInputStream(f));
log.info("Successfully loaded properties");
haveStatusProps = true;
} catch (FileNotFoundException e) {
log.info("Property file not found");
} catch (IOException e) {
log.info("Property file error " + e.toString());
}
}
}
/**
* the cache of TCP Connections
*/
private static ThreadLocal tp = new ThreadLocal() {
protected Object initialValue() {
return new HashMap();
}
};
private transient ProducerClient protocolHandler;
/**
* Constructor for ProducerSampler object.
*/
public ProducerSampler() {
log.debug("Created " + this);
protocolHandler = getProtocol();
log.debug("Using Protocol Handler: " + protocolHandler.getClass().getName());
}
/**
* @return Returns String object containing the error.
*/
private String getError() {
Map cp = (Map) tp.get();
return (String) cp.get(ERRKEY);
}
/**
* @return Returns the username.
*/
public String getUsername() {
return getPropertyAsString(ConfigTestElement.USERNAME);
}
/**
* @return Returns the password.
*/
public String getPassword() {
return getPropertyAsString(ConfigTestElement.PASSWORD);
}
/**
* @param newFilename - the new filename to set.
*/
public void setFilename(String newFilename) {
this.setProperty(FILENAME, newFilename);
}
/**
* @return Returns the filename.
*/
public String getFilename() {
return getPropertyAsString(FILENAME);
}
/**
* @return Returns the timeout int object.
*/
public int getTimeout() {
return TIMEOUT;
}
/**
* @return Returns if there was a delay.
*/
public boolean getNoDelay() {
return NODELAY;
}
/**
* @param newURL - the new url to set.
*/
public void setURL(String newURL) {
this.setProperty(URL, newURL);
}
/**
* @return Returns the url.
*/
public String getURL() {
return getPropertyAsString(URL);
}
/**
* @param newDuration - the new duration to set.
*/
public void setDuration(String newDuration) {
this.setProperty(DURATION, newDuration);
}
/**
* @return Returns the duration.
*/
public int getDuration() {
return getPropertyAsInt(DURATION);
}
/**
* @param newRampUp - the new ramp up to set.
*/
public void setRampUp(String newRampUp) {
this.setProperty(RAMP_UP, newRampUp);
}
/**
* @return Returns the ramp up.
*/
public int getRampUp() {
return getPropertyAsInt(RAMP_UP);
}
/**
* @param newDurable - the new durable to set.
*/
public void setDurable(String newDurable) {
this.setProperty(DURABLE, newDurable);
}
/**
* @return Returns whether message is durable.
*/
public boolean getDurable() {
return getPropertyAsBoolean(DURABLE);
}
/**
* @param newTopic - the new topic to set.
*/
public void setTopic(String newTopic) {
this.setProperty(TOPIC, newTopic);
}
/**
* @return Return whether the message is topic.
*/
public boolean getTopic() {
return getPropertyAsBoolean(TOPIC);
}
/**
* @param newMsgSize - the new message size to set.
*/
public void setMsgSize(String newMsgSize) {
this.setProperty(MSGSIZE, newMsgSize);
}
/**
* @return Returns the message size.
*/
public int getMsgSize() {
return getPropertyAsInt(MSGSIZE);
}
/**
* @param newNoProd - the number of producer to set.
*/
public void setNoProd(String newNoProd) {
this.setProperty(NOPROD, newNoProd);
}
/**
* @return Returns the number of producers.
*/
public int getNoProd() {
return getPropertyAsInt(NOPROD);
}
/**
* @param newNoSubject - the new number of subject to set.
*/
public void setNoSubject(String newNoSubject) {
this.setProperty(NOSUBJECT, newNoSubject);
}
/**
* @return Return the number of subject.
*/
public int getNoSubject() {
return getPropertyAsInt(NOSUBJECT);
}
/**
* Returns a formatted string label describing this sampler
* Example output:
* Tcp://Tcp.nowhere.com/pub/README.txt
*
* @return a formatted string label describing this sampler
*/
public String getLabel() {
return (this.getURL());
}
/**
* Return the object producer class name.
*
* @return Returns the classname of the producer.
*/
private static String getClassname() {
String className = JMeterUtils.getPropDefault("tcp.prod.handler", "ProducerClientImpl");
return className;
}
private static final String protoPrefix = "org.codehaus.activemq.producer.sampler.";
/**
* Returns the Class object of the running producer.
*
* @param className
* @return
*/
private Class getClass(String className) {
Class c = null;
try {
c = Class.forName(className
, false, Thread.currentThread().getContextClassLoader());
} catch (ClassNotFoundException e) {
try {
c = Class.forName(protoPrefix + className
, false, Thread.currentThread().getContextClassLoader());
} catch (ClassNotFoundException e1) {
log.error("Could not find protocol class " + className);
}
}
return c;
}
/**
* Retrieves the protocol.
*
* @return Returns the protocol.
*/
private ProducerClient getProtocol() {
ProducerClient ProducerClient = null;
Class javaClass = getClass(getClassname());
try {
ProducerClient = (ProducerClient) javaClass.newInstance();
if (log.isDebugEnabled()) {
log.debug(this
+ "Created: "
+ getClassname()
+ "@"
+ Integer.toHexString(ProducerClient.hashCode()));
}
} catch (Exception e) {
log.error(this + " Exception creating: " + getClassname(), e);
}
return ProducerClient;
}
/**
* Retrieves the sample as SampleResult object. There are times that this
* is ignored.
*
* @param e - Entry object.
* @return Returns the sample result.
*/
public SampleResult sample(Entry e) {// Entry tends to be ignored ...
log.debug(getLabel() + " " + getFilename() + " " + getUsername() + " " + getPassword());
SampleResult res = new SampleResult();
res.setSampleLabel(getName());
res.setSamplerData(getURL());
res.sampleStart();
try {
//run the benchmark tool code
this.run();
} catch (Exception ex) {
log.debug("", ex);
res.setResponseCode("500");
res.setResponseMessage(ex.toString());
}
//Calculate response time
res.sampleEnd();
// Set if we were successful or not
res.setSuccessful(true);
return res;
}
/**
* Checks if there is a response in the server.
*
* @param rc response code
* @return whether this represents success or not
*/
private boolean checkResponseCode(String rc) {
if ((rc.compareTo("400") >= 0) && (rc.compareTo("499") <= 0)) {
return false;
}
if (rc.compareTo("500") >= 0 && rc.compareTo("599") <= 0) {
return false;
}
return true;
}
/**
* Disconnects the session to the server.
*/
private void disconnectAll() {
synchronized (allSockets) {
Iterator i = allSockets.iterator();
while (i.hasNext()) {
Socket socket = (Socket) i.next();
try {
socket.close();
} catch (IOException e) {
log.warn("Error closing socket ", e);
} finally {
i.remove();
}
}
}
}
/**
* Logs the start of the test. This is called only once
* per class.
*/
public void testStarted() {
log.debug(this + " test started");
}
/**
* Logs the end of the test. This is called only once per
* class.
*/
public void testEnded() {
log.debug(this + " test ended");
disconnectAll();
}
/**
* Logs the host at the start of the test.
*
* @param host - the host to be logged.
*/
public void testStarted(String host) {
log.debug(this + " test started on " + host);
}
/**
* Logs the host at the end of the test.
*
* @param host - the host to be logged.
*/
public void testEnded(String host) {
log.debug(this + " test ended on " + host);
disconnectAll();
}
/**
* Logs the iteration event.
*
* @param event
*/
public void testIterationStart(LoopIterationEvent event) {
log.debug(this + " test iteration start on " + event.getIteration());
}
/**
* Runs and publish the message.
*
* @throws Exception
*/
public void run() throws Exception {
start();
publish();
}
/**
* Creates the subject that will be published.
*/
public void start() {
//create the subjects
subjects = new String[getNoSubject()];
for (int i = 0; i < getNoSubject(); i++) {
subjects[i] = TOOL_DEFAULT + i;
}
// set the duration
if (getDuration() == 0) {
duration = DEFAULT_DURATION;
} else {
duration = getDuration();
}
//set the ramp_up
if (getRampUp() == 0) {
ramp_up = DEFAULT_RAMP_UP;
} else {
ramp_up = getRampUp();
}
}
/**
* Retrieves the message then sends it via tcp.
*
* @throws Exception
*/
protected void publish() throws Exception {
final String text = getMessage();
for (int i = 0; i < getNoProd(); i++) {
final String subject = subjects[i % getNoSubject()];
Thread thread = new Thread() {
public void run() {
try {
publish(text, subject);
} catch (JMSException e) {
//System.out.println("Caught: " + e);
e.printStackTrace();
}
}
};
thread.start();
}
}
/**
* @return Returns the message.
*/
protected String getMessage() {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < getMsgSize(); i++) {
char ch = 'X';
buffer.append(ch);
}
return buffer.toString();
}
/**
* Sends the information from the client via tcp.
*
* @param text - message that is sent.
* @param subject - subject of the message to be sent.
* @throws JMSException
*/
protected void publish(String text, String subject) throws JMSException {
Session session = createSession();
Destination destination = createDestination(session, subject);
MessageProducer publisher = session.createProducer(destination);
if (getDurable()) {
publisher.setDeliveryMode(DeliveryMode.PERSISTENT);
} else {
publisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
}
if (loops <= 0) {
while (true) {
publishLoop(session, publisher, text);
}
} else {
for (int i = 0; i < loops; i++) {
publishLoop(session, publisher, text);
}
}
}
/**
* Creates a new Session object.
*
* @return Returns a new Session object.
* @throws JMSException
*/
protected Session createSession() throws JMSException {
if (factory == null) {
factory = createFactory();
}
Connection connection = factory.createConnection();
int value = connectionCounter.increment();
if (getDurable()) {
connection.setClientID(idGenerator.generateId());
}
addResource(connection);
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
addResource(session);
return session;
}
/**
* Creates a Destination object through Session using subject.
*
* @param session - Session used to create the Destination.
* @param subject - the subject of the Destination to be created.
* @return Returns Destination object.
* @throws JMSException
*/
protected Destination createDestination(Session session, String subject) throws JMSException {
if (getTopic()) {
return session.createTopic(subject);
} else {
return session.createQueue(subject);
}
}
/**
* Sends a message through MessageProducer objects.
*
* @param session - Session oject.
* @param publisher - MessageProducer object.
* @param text - text that is used to create Message object.
* @throws JMSException
*/
protected void publishLoop(Session session, MessageProducer publisher, String text) throws JMSException {
for (int i = 0; i < loopSize; i++) {
Message message = session.createTextMessage(text);
publisher.send(message);
count(1);
}
}
/**
* Creates an instance of ActiveMQConnectionFactory object and
* sets value to the new objects useEmbeddedBroker variable
* depending on the its instance variable embeddedBroker.
*
* @return Instance of ActiveMQConnectionFactory object.
*/
protected ActiveMQConnectionFactory createFactory() {
ActiveMQConnectionFactory answer = new ActiveMQConnectionFactory(getURL());
if (embeddedBroker) {
answer.setUseEmbeddedBroker(true);
}
return answer;
}
/**
* Add a new object to resource.
*
* @param resource - Object added to resource.
*/
protected void addResource(Object resource) {
resources.add(resource);
}
/**
* Increments the int variable.
*
* @param count - variable incremented.
*/
protected synchronized void count(int count) {
counter += count;
}
/**
* @return the current number of messages sent.
*/
public static synchronized int resetCount() {
int answer = counter;
counter = 0;
return answer;
}
}