Simulator for JMS based endpoints

Testing components which communicate over JMS can quickly become a complex task. It is seldom sufficient to simply verify that a given message is sent to an endpoint. Prevalent case is scenario that involves producing messages on multiple sides of interaction. There can be a number of message types required to be sent to fulfill specific application-level communication protocol.
Obviously, most of the test cases can be verified by dividing the complex suite into more granular parts that cover single, cohesive unit of work. Yet, more often than not I find myself facing the same problem all over again – simulating JMS based endpoint in a stateful communication.
For HTTP-based APIs there is a robust mocking framework – Wiremock. It allows you to mimic very sophisticated use-cases and eventually make your application more resilient. It’s definitely something worth spending some time on. Unfortunately, no such utility exists for JMS yet. As I will present further on, it’s relatively easy to prototype something similar that covers bare minimum of functionality. To take advantage of existing components and available framework support, Spring will be used as configuration provider.
The following code snippet presents a proof-of-concept of a mocking endpoint for JMS communication. To get a full grasp of configuration you might want to refer to my previous post on integration testing JMS components.

@Test
public void shouldPrintTwoReceivedMessagesAndRespondToOne() throws Exception {
    mockDestination
            .whenReceived(message -> message.equals(FIRST_MESSAGE))
            .thenSend(RESPONSE_QUEUE, message -> message + " acknowledged");
    mockDestination
            .whenReceived(message -> message.startsWith("A"))
            .thenConsume(message -> System.out.println("Received: " + message));

    send(FIRST_MESSAGE);
    send(SECOND_MESSAGE);

    System.out.println(jmsTemplate.receiveAndConvert(RESPONSE_QUEUE));
}

@Bean
Gateway<String> mockDestination(JmsTemplate jmsTemplate) {
    return new MockDestination<>(jmsTemplate, Function.identity());
}

The test is based on MockDestination class which uses Spring’s JmsTemplate underneath. First a set of rules is recorded: when the endpoint receives FIRST_MESSAGE on the REQUEST_QUEUE, it should respond appropriately to the RESPONSE_QUEUE. Furthermore, if a received message starts with the letter A, it should be printed to the standard output.
Mock destination implementation consists of set of predicates with potentially multiple ordered actions. When a given predicate is met, actions are triggered. Additionally, destination exposes methods to mimic sending and receiving messages to make it allow manual flow manipulation.

@Slf4j
class MockDestination<T> implements Gateway<T> {
    private final List<WhenReceived<T>> listeners = new ArrayList<>();

    private final JmsTemplate jmsTemplate;

    private final Function<String, T> fromTextToMessageConverter;

    MockDestination(JmsTemplate jmsTemplate, Function<String, T> fromTextToMessageConverter) {
        this.jmsTemplate = jmsTemplate;
        this.fromTextToMessageConverter = fromTextToMessageConverter;
    }

    @Override
    public void receive(T message) {
        listeners.forEach(consumer -> consumer.onMessage(message));
    }

    @Override
    public void send(String destination, String message) {
        jmsTemplate.convertAndSend(destination, message);
    }

    @JmsListener(destination = "RequestQueue", containerFactory = "defaultJmsListenerContainerFactory")
    private void onMessage(TextMessage message) throws JMSException {
        log.info("onMessage");
        log.debug("onMessage - Message: {}", message);

        try {
            String messageText = message.getText();

            receive(fromTextToMessageConverter.apply(messageText));
        } catch (JMSException e) {
            log.error("Cannot consume message: {}", message, e);
        }
    }

    public WhenReceived<T> whenReceived(Predicate<T> messageMatcher) {
        WhenReceived<T> whenReceived = new WhenReceived<>(this, messageMatcher);
        listeners.add(whenReceived);
        return whenReceived;
    }
}

This simple component might not be very generic at the moment and certainly requires a great dose of improvements to be reusable, but it a good starting point for future work.

The complete solution is available on Github.

String concatenation with null

The previous post concisely presented how to observe default values of static fields set during preparation phase of class loading. An integer primitive type was used as an example. Very similar behavior was described in Puzzle 49: Larger Than Life from Java Puzzlers Traps, Pitfalls, and Corner Cases by Joshua Bloch and Neal Gafter.
When it comes to reference types, the default value is null, which upon dereferencing can result in a runtime exception. Let’s consider the class introduced in the previous post but this time with a static field of String type.

public class UnexpectedStringValue {
    static final UnexpectedStringValue instance = new UnexpectedStringValue();

    static String DEFAULT_VALUE = "world";

    String member;

    public UnexpectedStringValue() {
        this.member = "Hello " + DEFAULT_VALUE;
    }

    public static void main(String[] args) {
        System.out.println(instance.member);
    }
}

During the execution on the main method from the class UnexpectedStringValue Hello null is printed to the standard output. Java compiler preemptively replaces explicit single-scope string concatenation with a chain of StringBuilder.append() calls. In the presented snippet javac has easy task to do, because type of DEFAULT_VALUE is known upfront. Consequently, the execution is dispatched to StringBuilder.append(String). Taking a peek at generated bytecode confirms this hypothesis. Excerpt from decompiled class file corresponds to string concatenation from UnexpectedStringValue‘s constructor.

INVOKESPECIAL java/lang/StringBuilder.<init> ()V
LDC "Hello "
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
GETSTATIC pl/ciruk/blog/preparation/UnexpectedStringValue.DEFAULT_VALUE : Ljava/lang/String;
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;

What would compiler do if the expression from constructor was altered to append null directly? The following code can be compiled successfully and when executed produces the same result as the code before.

public UnexpectedStringValue() {
    this.member = "Hello " + null;
}

What did compiler actually do? This time the task was more complicated. To understand why, take a look at the following expression, which is not valid and compiler would complain with a message informing about ambiguous method reference.

new StringBuilder().append(null);

Upon inspection of bytecode generated for "Hello " + null we can see, that yet another overloaded method was called.

ACONST_NULL
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/Object;)Ljava/lang/StringBuilder;

Compiler must have applied a trick to handle null reference as a reference of Object type. This kind of action is dictated by JLS 15.18.1 and 5.1.11, which states that during concatenation of strings, null references gets converted to string "null".