
org.perfcake.message.generator.SenderTask Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of perfcake Show documentation
Show all versions of perfcake Show documentation
A Lightweight Performance Testing Framework
/*
* -----------------------------------------------------------------------\
* PerfCake
*
* Copyright (C) 2010 - 2016 the original author or authors.
*
* 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.perfcake.message.generator;
import org.perfcake.PerfCakeConst;
import org.perfcake.PerfCakeException;
import org.perfcake.message.Message;
import org.perfcake.message.MessageTemplate;
import org.perfcake.message.ReceivedMessage;
import org.perfcake.message.correlator.Correlator;
import org.perfcake.message.sender.MessageSender;
import org.perfcake.message.sender.MessageSenderManager;
import org.perfcake.message.sequence.SequenceManager;
import org.perfcake.reporting.MeasurementUnit;
import org.perfcake.reporting.ReportManager;
import org.perfcake.reporting.ReportingException;
import org.perfcake.validation.ValidationManager;
import org.perfcake.validation.ValidationTask;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Executes a single task of sending messages from the message store
* using instances of {@link MessageSender} provided by message sender manager (see {@link org.perfcake.message.sender.MessageSenderManager}),
* receiving the message sender's response and handling the reporting and response message validation.
* Sender task is not part of the public API, it is used from generators.
*
* @author Pavel Macík
* @author Martin Večeřa
* @see org.perfcake.message.sender.MessageSenderManager
*/
public class SenderTask implements Runnable {
/**
* Sender task's logger.
*/
private static final Logger log = LogManager.getLogger(SenderTask.class);
/**
* Limit the number of warning about interrupted response receival at the end of the test.
*/
private static volatile AtomicInteger interruptWarningDisplayed = new AtomicInteger(0);
/**
* Reference to a message sender manager that is providing the message senders.
*/
private MessageSenderManager senderManager;
/**
* Reference to a message store where the messages are taken from.
*/
private List messageStore;
/**
* Reference to a report manager.
*/
private ReportManager reportManager;
/**
* A reference to the current validator manager. It is used to validate message responses.
*/
private ValidationManager validationManager;
/**
* A reference to the current sequence manager. This is used for determining message specific sequence values.
*/
private SequenceManager sequenceManager;
/**
* Message generator that created this sender task.
*/
private final MessageGenerator messageGenerator;
/**
* The time when the task was enqueued.
*/
private long enqueueTime = System.nanoTime();
/**
* Correlator to correlate message received from a separate channel.
*/
private Correlator correlator = null;
/**
* A response matched by a correlator.
*/
private Serializable correlatedResponse = null;
/**
* Synchronize on waiting for a message from a correlator.
* Must be set before correlator is used.
*/
private Semaphore waitForResponse;
/**
* Creates a new task to send a message.
* There is a communication channel established that allows and requires the sender task to report the task completion and any possible error.
* The visibility of this constructor is limited as it is not intended for normal use.
* To obtain a new instance of a sender task properly initialized call
* {@link AbstractMessageGenerator#newSenderTask()}.
*
* @param messageGenerator
* The message generator that created this sender task.
*/
protected SenderTask(final MessageGenerator messageGenerator) {
this.messageGenerator = messageGenerator;
}
/**
* Reports an exception from a sender when the generator is supposed to fail fast. This terminates test execution.
*
* @param e
* The error from the sender to be reported.
*/
private void reportSenderError(final Exception e) {
if (messageGenerator.isFailFast()) {
messageGenerator.interrupt(e);
}
}
private Serializable sendMessage(final MessageSender sender, final Message message, final Properties messageAttributes, final MeasurementUnit mu) {
try {
sender.preSend(message, messageAttributes);
} catch (final Exception e) {
if (log.isErrorEnabled()) {
log.error("Unable to initialize sending of a message: ", e);
}
reportSenderError(e);
}
mu.startMeasure();
Serializable result = null;
try {
result = sender.send(message, mu);
} catch (final Exception e) {
mu.setFailure(e);
if (log.isErrorEnabled()) {
log.error("Unable to send a message: ", e);
}
reportSenderError(e);
}
mu.stopMeasure();
try {
sender.postSend(message);
} catch (final Exception e) {
if (log.isErrorEnabled()) {
log.error("Unable to finish sending of a message: ", e);
}
reportSenderError(e);
}
return result;
}
/**
* Executes the scheduled sender task. This is supposed to be controlled by an enclosing thread.
*/
@Override
public void run() {
assert messageStore != null && reportManager != null && validationManager != null && senderManager != null : "SenderTask was not properly initialized.";
final Properties messageAttributes = sequenceManager != null ? sequenceManager.getSnapshot() : new Properties();
MessageSender sender = null;
ReceivedMessage receivedMessage;
try {
final MeasurementUnit mu = reportManager.newMeasurementUnit();
long requestSize = 0;
long responseSize = 0;
if (mu != null) {
mu.setEnqueueTime(enqueueTime);
if (messageAttributes != null) {
mu.appendResult(PerfCakeConst.ATTRIBUTES_TAG, messageAttributes);
messageAttributes.put(PerfCakeConst.ITERATION_NUMBER_PROPERTY, String.valueOf(mu.getIteration()));
}
mu.appendResult(PerfCakeConst.THREADS_TAG, reportManager.getRunInfo().getThreads());
sender = senderManager.acquireSender();
final Iterator iterator = messageStore.iterator();
if (iterator.hasNext()) {
while (iterator.hasNext()) {
final MessageTemplate messageToSend = iterator.next();
final Message currentMessage = messageToSend.getFilteredMessage(messageAttributes);
final long multiplicity = messageToSend.getMultiplicity();
requestSize = requestSize + (currentMessage.getPayload().toString().length() * multiplicity);
for (int i = 0; i < multiplicity; i++) {
if (correlator != null) {
correlator.registerRequest(this, currentMessage, messageAttributes);
sendMessage(sender, currentMessage, messageAttributes, mu);
waitForResponse.acquire(); // the only line throwing InterruptedException here
receivedMessage = new ReceivedMessage(correlatedResponse, messageToSend, currentMessage, messageAttributes);
} else {
receivedMessage = new ReceivedMessage(sendMessage(sender, currentMessage, messageAttributes, mu), messageToSend, currentMessage, messageAttributes);
}
if (receivedMessage.getResponse() != null) {
responseSize = responseSize + receivedMessage.getResponse().toString().length();
}
if (validationManager.isEnabled()) {
validationManager.submitValidationTask(new ValidationTask(Thread.currentThread().getName(), receivedMessage));
}
}
}
} else {
if (correlator != null) {
final String error = "Receiver and Correlator cannot be used without a message definition. There is no information to compute and store the correlation ID. At least, define a message with an empty content.";
log.error(error);
reportSenderError(new PerfCakeException(error));
} else {
receivedMessage = new ReceivedMessage(sendMessage(sender, null, messageAttributes, mu), null, null, messageAttributes);
if (receivedMessage.getResponse() != null) {
responseSize = responseSize + receivedMessage.getResponse().toString().length();
}
if (validationManager.isEnabled()) {
validationManager.submitValidationTask(new ValidationTask(Thread.currentThread().getName(), receivedMessage));
}
}
}
senderManager.releaseSender(sender); // !!! important !!!
sender = null;
mu.appendResult(PerfCakeConst.REQUEST_SIZE_TAG, requestSize);
mu.appendResult(PerfCakeConst.RESPONSE_SIZE_TAG, responseSize);
reportManager.report(mu);
}
} catch (InterruptedException | PerfCakeException e) {
if (e instanceof InterruptedException) { // there is just one line of code that can throw this exception above
if (interruptWarningDisplayed.getAndIncrement() == 0) {
log.warn("Test execution interrupted while waiting for a response message from a receiver.");
}
} else {
log.error("Error sending message: ", e);
reportSenderError(e);
}
} finally {
if (sender != null) {
senderManager.releaseSender(sender);
}
}
}
/**
* Notifies the sender task of receiving a response from a separate message channel.
* This is called from {@link org.perfcake.message.correlator.Correlator} when {@link org.perfcake.message.receiver.Receiver} is used.
*
* @param response
* The response corresponding to the original request.
*/
public void registerResponse(final Serializable response) {
correlatedResponse = response;
waitForResponse.release();
}
/**
* Configures a {@link org.perfcake.message.sender.MessageSenderManager} for the sender task.
*
* @param senderManager
* {@link org.perfcake.message.sender.MessageSenderManager} to be used by the sender task.
*/
protected void setSenderManager(final MessageSenderManager senderManager) {
this.senderManager = senderManager;
}
/**
* Configures a message store for the sender task.
*
* @param messageStore
* Message store to be used by the sender task.
*/
protected void setMessageStore(final List messageStore) {
this.messageStore = messageStore;
}
/**
* Configures a {@link org.perfcake.reporting.ReportManager} for the sender task.
*
* @param reportManager
* {@link org.perfcake.reporting.ReportManager} to be used by the sender task.
*/
protected void setReportManager(final ReportManager reportManager) {
this.reportManager = reportManager;
}
/**
* Configures a {@link org.perfcake.validation.ValidationManager} for the sender task.
*
* @param validationManager
* {@link org.perfcake.validation.ValidationManager} to be used by the sender task.
*/
protected void setValidationManager(final ValidationManager validationManager) {
this.validationManager = validationManager;
}
/**
* Configures a {@link SequenceManager} for the sender task.
*
* @param sequenceManager
* {@link SequenceManager} to be used by the sender task.
*/
public void setSequenceManager(final SequenceManager sequenceManager) {
this.sequenceManager = sequenceManager;
}
/**
* Sets the correlator that is used to notify us about receiving a response from a separate message channel.
*
* @param correlator
* The correlator to be used.
*/
public void setCorrelator(final Correlator correlator) {
waitForResponse = new Semaphore(0);
this.correlator = correlator;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy