/* * -----------------------------------------------------------------------\ * 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.sender; import org.perfcake.PerfCakeException; import org.perfcake.message.Message; import org.perfcake.reporting.MeasurementUnit; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; /** * Serves as a dummy sender for scenario testing and developing purposes. It does not actually send any message. * It can simulate a synchronous waiting for a reply by setting the {@link #delay} property in milliseconds (with default values 0). * * @author <a href="mailto:pavel.macik@gmail.com">Pavel Macík</a> * @author <a href="mailto:marvenec@gmail.com">Martin Večeřa</a> */ public class TestSender extends AbstractSender { /** * The sender's logger. */ private static final Logger log = LogManager.getLogger(TestSender.class); /** * Iteration counter (how many times the doSend method has been called). */ private static AtomicLong counter = new AtomicLong(0); /** * Test the usage of thread locals. */ private ThreadLocal<Integer> localCounter = new ThreadLocal<>(); /** * Copy of thread local variables to see that this works properly. */ private static transient Map<String, Integer> localCounterValues = new ConcurrentHashMap<>(); /** * The delay duration to simulate a asynchronous waiting. */ private long delay = 0; /** * Can switch on recording of each message payload sent. */ private boolean recording = false; /** * Contains all recorded messages in case the {@link #recording} was switched on. */ private static List<String> recordedMessages = Collections.synchronizedList(new ArrayList<>()); /** * A listener where the computed target for each initialization is reported. */ private static Consumer<String> onInitListener = null; @Override public void doInit(final Properties messageAttributes) throws PerfCakeException { final String currentTarget = safeGetTarget(messageAttributes); if (log.isDebugEnabled()) { log.debug("Initializing... " + currentTarget); } if (onInitListener != null) { onInitListener.accept(currentTarget); } } /** * Sets a listener where current computed target address from calls to {@link #doInit(Properties)} are reported. * This method is not thread-safe, you can use it only before the scenario is started, or after it is finished. * Do not forget to cleanup your listener by passing null argument. * * @param listener * The listener where to report the target. */ public static void setOnInitListener(final Consumer<String> listener) { onInitListener = listener; } @Override public void doClose() { if (log.isDebugEnabled()) { log.debug("Closing..."); } // nop } @Override public void preSend(final Message message, final Properties messageAttributes) throws Exception { super.preSend(message, messageAttributes); if (log.isDebugEnabled()) { log.debug("Sending to " + safeGetTarget(messageAttributes) + "..."); } } @Override public Serializable doSend(final Message message, final MeasurementUnit measurementUnit) throws Exception { final long count = counter.incrementAndGet(); if (log.isDebugEnabled()) { log.debug("Dummy counter: " + count); } if (localCounter.get() == null) { localCounter.set(1); } else { localCounter.set(localCounter.get() + 1); } localCounterValues.put(Thread.currentThread().getName(), localCounter.get()); if (delay > 0) { final long sleepStart = System.currentTimeMillis(); try { Thread.sleep(delay); } catch (final InterruptedException ie) { // Snooze final long snooze = delay - (System.currentTimeMillis() - sleepStart); if (snooze > 0) { Thread.sleep(snooze); } } } if (recording && message != null) { recordedMessages.add(message.getPayload().toString()); } if (message != null && message.toString().contains("fail me please")) { throw new IOException("Failing per user request."); } return (message == null) ? null : message.getPayload(); } /** * Gets read the value of delay. * * @return The delay in milliseconds. */ public long getDelay() { return delay; } /** * Sets the value of delay. * * @param delay * The delay to set in milliseconds. * @return Instance of this for fluent API. */ public TestSender setDelay(final long delay) { this.delay = delay; return this; } /** * Obtains the current state of the recording. * * @return True iff recording of message payloads passed through this sender is switched on. */ public boolean isRecording() { return recording; } /** * Switches on recording of message payloads passed through this sender. * * @param recording * True iff the recording should be turned on. */ public void setRecording(final boolean recording) { this.recording = recording; } /** * Gets the thread local counter values. * * @return The thread local counter values. */ public Map<String, Integer> getLocalCounters() { return localCounterValues; } /** * Gets the list of recorded message payloads passed through this message sender while recording was switched on. * * @return The list of recorded message payloads passed through this message sender while recording was switched on. */ public static List<String> getRecordedMessages() { return recordedMessages; } /** * Clears all recorded messages. */ public static void resetRecordings() { recordedMessages.clear(); } /** * Resets the iteration counter (how many times the {@link #doSend(org.perfcake.message.Message, org.perfcake.reporting.MeasurementUnit)} method has been called). */ public static void resetCounter() { counter.set(0); } /** * Gets the iteration counter (how many times the {@link #doSend(org.perfcake.message.Message, org.perfcake.reporting.MeasurementUnit)} method has been called). * * @return The iteration counter value. */ public static long getCounter() { return counter.get(); } }