citrusframework / citrus-simulator

Standalone simulator for different messaging transports such as Http REST, SOAP WebService, JMS, RMI, mail messaging and more
Apache License 2.0
44 stars 45 forks source link

Got com.consol.citrus.exceptions.CitrusRuntimeException: Failed to get correlation key for 'citrus_message_correlator_simulatorJmsInboundEndpoint:consumer' while replying expected message through replyDestination queue for JMS simulator #101

Closed komyos closed 3 years ago

komyos commented 3 years ago

I would like to use citrus simulator as MQ mock for integration test. I implemented IBM MQ instead of ActiveMQ provided on example, everything works fine, citrus simulator is able to connect to IBM MQ cloud and get messages from particular queue but got an error occurred while sending response message

error com.consol.citrus.exceptions.CitrusRuntimeException: Failed to get correlation key for 'citrus_message_correlator_simulatorJmsInboundEndpoint:consumer''

pom.xml

<dependencies>
  <!--Basic-->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
  </dependency>
  <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
  </dependency>
  <dependency>
    <groupId>org.jolokia</groupId>
    <artifactId>jolokia-core</artifactId>
  </dependency>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
  </dependency>

  <!--Others-->
  <dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
  </dependency>
  <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.2</version>
  </dependency>

  <!-- Micrometer core dependency  -->
  <dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-core</artifactId>
  </dependency>
  <!-- Micrometer Prometheus registry  -->
  <dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
  </dependency>

  <!-- Citrus Simulator -->
  <dependency>
    <groupId>com.consol.citrus</groupId>
    <artifactId>citrus-simulator-starter</artifactId>
    <version>1.1.0</version>
  </dependency>
  <dependency>
    <groupId>com.consol.citrus</groupId>
    <artifactId>citrus-simulator-ui</artifactId>
    <version>1.1.0</version>
  </dependency>

  <!-- Citrus -->
  <dependency>
    <groupId>com.consol.citrus</groupId>
    <artifactId>citrus-jms</artifactId>
    <version>2.8.0</version>
  </dependency>

  <dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>7.4.0</version>
    <scope>test</scope>
  </dependency>

  <!--IBM MQ borker -->
  <dependency>
    <groupId>com.ibm.mq</groupId>
    <artifactId>com.ibm.mq.allclient</artifactId>
    <version>9.1.1.0</version>
  </dependency>
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
  </dependency>

</dependencies>

application.properties

logging.level.com.consol.citrus=DEBUG

# Default timeout setting
citrus.simulator.defaultTimeout=10000
# Default message template path
citrus.simulator.templatePath=templates
# Default scenario name
citrus.simulator.defaultScenario=Default
# Should Citrus validate incoming messages on syntax and semantics
citrus.simulator.templateValidation=false

# Enable JMS support
citrus.simulator.jms.enabled=true
citrus.simulator.jms.synchronous=true
citrus.simulator.jms.inboundDestination=DEV.QUEUE.1
citrus.simulator.jms.replyDestination=DEV.QUEUE.2

citrus-simulator-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:citrus="http://www.citrusframework.org/schema/config"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

       http://www.citrusframework.org/schema/config http://www.citrusframework.org/schema/config/citrus-config.xsd">

    <citrus:schema-repository id="schemaRepository">
        <citrus:locations>
            <citrus:location path="classpath:xsd/HelloService.xsd"/>
        </citrus:locations>
    </citrus:schema-repository>

    <citrus:global-variables>
        <citrus:variable name="simulator.name" value="Citrus JMS Simulator"/>
    </citrus:global-variables>

    <citrus:namespace-context>
        <citrus:namespace prefix="citrus" uri="http://citrusframework.org"/>
        <citrus:namespace prefix="hello" uri="http://citrusframework.org/schemas/hello"/>
    </citrus:namespace-context>

</beans>

SimulatorJmsConfig.java

public class SimulatorJmsConfig extends SimulatorJmsAdapter {
  @SneakyThrows(JMSException.class)
  @Override
  public ConnectionFactory connectionFactory() {
      val mqConnFactory = new MQConnectionFactory();

      mqConnFactory.setStringProperty(WMQConstants.WMQ_HOST_NAME, MQ_HOST);
      mqConnFactory.setIntProperty(WMQConstants.WMQ_PORT, MQ_PORT);
      mqConnFactory.setStringProperty(WMQConstants.WMQ_CHANNEL, MQ_CHANNEL);
      mqConnFactory.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
      mqConnFactory.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, MQ_QUEUE_MANAGER);
      mqConnFactory.setStringProperty(WMQConstants.WMQ_APPLICATIONNAME, MQ_APP_NAME);
      mqConnFactory.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, true);
      mqConnFactory.setStringProperty(WMQConstants.USERID, MQ_USER_ID);
      mqConnFactory.setStringProperty(WMQConstants.PASSWORD, MQ_API_KEY);

      return mqConnFactory;
  }
}

for templates, xsd, scenarios and starters, I just copied from sample-jms but different packages (not com.consol.citrus.simulator.sample)

komyos commented 3 years ago

I already fixed this issue, just need to provide JMSReplyTo with the destination queue you want to get message back

@Test
void testMqMock() {
    // Initializes MQ service object.
    log.info("Initializing MQ objects...");
    val mqConnFactory = new MQConnectionFactory();
    mqConnFactory.setStringProperty(WMQConstants.WMQ_HOST_NAME, MQ_HOST);
    mqConnFactory.setIntProperty(WMQConstants.WMQ_PORT, MQ_PORT);
    mqConnFactory.setStringProperty(WMQConstants.WMQ_CHANNEL, MQ_CHANNEL);
    mqConnFactory.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
    mqConnFactory.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, MQ_QUEUE_MANAGER);
    mqConnFactory.setStringProperty(WMQConstants.WMQ_APPLICATIONNAME, MQ_APP_NAME);
    mqConnFactory.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, true);
    mqConnFactory.setStringProperty(WMQConstants.USERID, MQ_USER_ID);
    mqConnFactory.setStringProperty(WMQConstants.PASSWORD, MQ_API_KEY);

    try {
        // Connects to MQ Cloud.
        log.info("Connecting to IBM MQ Cloud...");
        val mqConn = mqConnFactory.createConnection();

        // Initializes session.
        val mqSession = mqConn.createSession(false, Session.AUTO_ACKNOWLEDGE);

        // Initializes producer.
        val mqProducer = mqSession.createProducer(mqSession.createQueue(String.format("queue:///%s", MQ_QUEUE_NAME)));

        // Publishes a message to MQ.
        log.info("Publishing a message to MQ...");
        val mqMessage = mqSession.createTextMessage(this.expectedMessage);
        val id = UUID.randomUUID().toString();
        mqMessage.setJMSCorrelationID(id);
        mqMessage.setJMSMessageID(id);
        mqMessage.setJMSType("MQ Mock");
        mqMessage.setJMSReplyTo(mqSession.createQueue(String.format("queue:///%s", MQ_QUEUE_NAME_REPLY))); // <-- This is what we need to set

        try {
            mqConn.start();
            mqProducer.send(mqMessage);
        } finally {
            mqConn.stop();
        }

        Assertions.assertDoesNotThrow(() -> log.info("Succeeded."));
    } catch (JMSException e) {
        log.error("There is an error occurred while processing MQ.", e);
        Assertions.fail(e);
    }
}