diff --git a/index.html b/index.html index 70c3a8757..60ffd558e 100644 --- a/index.html +++ b/index.html @@ -476,6 +476,11 @@

Citrus Simulator Documentation

PDF

+

2.3.0-SNAPSHOT

+

HTML

+

PDF

+ +

2.1.0

HTML

PDF

@@ -546,7 +551,7 @@

Contribute changes

diff --git a/reference/2.1.1/html/images/citrus-logo-small.png b/reference/2.1.1/html/images/citrus-logo-small.png new file mode 100644 index 000000000..84b439864 Binary files /dev/null and b/reference/2.1.1/html/images/citrus-logo-small.png differ diff --git a/reference/2.1.1/html/images/citrus-logo-tiny.png b/reference/2.1.1/html/images/citrus-logo-tiny.png new file mode 100644 index 000000000..cd3f6e134 Binary files /dev/null and b/reference/2.1.1/html/images/citrus-logo-tiny.png differ diff --git a/reference/2.1.1/html/images/citrus-logo.png b/reference/2.1.1/html/images/citrus-logo.png new file mode 100644 index 000000000..9faa3353e Binary files /dev/null and b/reference/2.1.1/html/images/citrus-logo.png differ diff --git a/reference/2.1.1/html/images/dashboard.png b/reference/2.1.1/html/images/dashboard.png new file mode 100644 index 000000000..d4a57fdba Binary files /dev/null and b/reference/2.1.1/html/images/dashboard.png differ diff --git a/reference/2.1.1/html/images/default-ui.png b/reference/2.1.1/html/images/default-ui.png new file mode 100644 index 000000000..204efeb08 Binary files /dev/null and b/reference/2.1.1/html/images/default-ui.png differ diff --git a/reference/2.1.1/html/images/messages.png b/reference/2.1.1/html/images/messages.png new file mode 100644 index 000000000..07a328840 Binary files /dev/null and b/reference/2.1.1/html/images/messages.png differ diff --git a/reference/2.1.1/html/images/scenario-activity-details.png b/reference/2.1.1/html/images/scenario-activity-details.png new file mode 100644 index 000000000..b5c04bc67 Binary files /dev/null and b/reference/2.1.1/html/images/scenario-activity-details.png differ diff --git a/reference/2.1.1/html/images/scenario-activity-list.png b/reference/2.1.1/html/images/scenario-activity-list.png new file mode 100644 index 000000000..4df78a1c2 Binary files /dev/null and b/reference/2.1.1/html/images/scenario-activity-list.png differ diff --git a/reference/2.1.1/html/images/scenario-details.png b/reference/2.1.1/html/images/scenario-details.png new file mode 100644 index 000000000..372ca9f0b Binary files /dev/null and b/reference/2.1.1/html/images/scenario-details.png differ diff --git a/reference/2.1.1/html/images/scenario-list.png b/reference/2.1.1/html/images/scenario-list.png new file mode 100644 index 000000000..70a6ee84d Binary files /dev/null and b/reference/2.1.1/html/images/scenario-list.png differ diff --git a/reference/2.1.1/html/images/scenario-starter-details.png b/reference/2.1.1/html/images/scenario-starter-details.png new file mode 100644 index 000000000..33d8cce6f Binary files /dev/null and b/reference/2.1.1/html/images/scenario-starter-details.png differ diff --git a/reference/2.1.1/html/images/scenario-starter-list.png b/reference/2.1.1/html/images/scenario-starter-list.png new file mode 100644 index 000000000..8e41c5138 Binary files /dev/null and b/reference/2.1.1/html/images/scenario-starter-list.png differ diff --git a/reference/2.1.1/html/images/sponsor-logo.png b/reference/2.1.1/html/images/sponsor-logo.png new file mode 100644 index 000000000..a2a1c9c1e Binary files /dev/null and b/reference/2.1.1/html/images/sponsor-logo.png differ diff --git a/reference/2.1.1/html/index.html b/reference/2.1.1/html/index.html new file mode 100644 index 000000000..02ff7665e --- /dev/null +++ b/reference/2.1.1/html/index.html @@ -0,0 +1,3993 @@ + + + + + + + + +Citrus Simulator + + + + + + + +
+
+
+
+

Version: 2.1.1

+
+
+

citrus-logo

+
+ +
+
+
+

1. Introduction

+
+
+

This is a standalone simulator for different messaging transports such as Http REST, SOAP WebService, +JMS, RMI, mail messaging and more.

+
+
+

The simulator provides server APIs for clients and responds with predefined messages according to +defined scenarios. The simulator response logic is very powerful and enables us to simulate any kind of server interface.

+
+
+

The simulator uses Spring Boot in combination with the test framework Citrus. You can +code simulator scenarios in Java or XML. Each scenario execution on the simulator is stored to an internal database and can be reviewed via web UI in the browser.

+
+
+

Feedback and contributions are highly appreciated!

+
+
+

1.1. Project status

+
+

NOTE: This project is considered stable but still under construction!

+
+
+

The simulator application is stable not yet finished. Some features are still under construction. Some aspects are simply not covered yet. Please see the following experimental +features.

+
+
+
Experimental features
+ + + + + + + + + + + + + +
+WSDL generated scenarios + +

Auto generate simulator scenarios from WSDL file

+
+Swagger generated scenarios + +

Auto generate simulator scenarios from Swagger API file

+
+Combined simulator + +

Use multiple message transports in one simulator instance

+
+
+
+

Following from that we have to deal with some limitations and trade offs until the project emerges (hopefully with the help of the community, keeping our fingers crossed!). +However the simulator is usable and all main features are considered to be stable.

+
+
+
+

1.2. Prerequisites

+
+

You may want to check the following prerequisites for using the simulator application.

+
+
+

1.2.1. Java 8

+
+

The simulator is a Java application coded in Java 8. Following from that you need at least Java 8 to run it as a Spring Boot web application. +Please make sure that you have Java development kit installed and set up. You can verify this with this command in a new terminal window.

+
+
+
+
java -version
+
+
+
+
+

1.2.2. Browser

+
+

The simulator provides a small web user interface when started. You can access this web UI with your browser. As we are in an early state in this project we do not +invest much time in full cross-browser compatibility. We use Chrome and Firefox during development. So the simulator application is most likely to be 100% working +on these two browsers. Of course other browsers might work without any limitations, too.

+
+
+
+
+
+
+

2. Installation

+
+
+

The Citrus simulator is a web application that uses Spring boot and Angular2. The simulator is able to run as Java application on your local machine +or as container/pod in Docker, Kubernetes or Openshift.

+
+
+

You can use any build system you like when building the simulator application. Here we show how to setup everything using Gradle and Maven.

+
+
+

2.1. Build with Gradle

+
+

Gradle uses groovy build scripts that we have to add first when starting a new simulator project.

+
+
+
+
buildscript {
+    repositories {
+        mavenCentral()
+    }
+    dependencies {
+        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.6.RELEASE")
+    }
+}
+
+apply plugin: 'java'
+apply plugin: 'eclipse'
+apply plugin: 'idea'
+apply plugin: 'org.springframework.boot'
+
+jar {
+    group = "org.citrusframework.simulator"
+    baseName = 'citrus-simulator-sample'
+    version =  '1.0.0'
+}
+
+repositories {
+    mavenCentral()
+}
+
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
+
+dependencies {
+    implementation("org.springframework.boot:spring-boot-starter-web")
+    implementation("org.citrusframework:citrus-simulator-starter:1.0.0")
+    testImplementation("junit:junit")
+}
+
+
+
+

The above build script will setup the Spring Boot dependencies for you. In addition to that we add the basic citrus-simulator-starter dependency. That’s it for the Gradle build. +We are ready to build and run the simulator now.

+
+
+

Before we do that let’s add a basic Spring Boot main class to our project.

+
+
+
src/main/java/com/consol/citrus/simulator/Simulator.java
+
+
package org.citrusframework.simulator;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Simulator {
+
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+}
+
+
+
+

Also we add an default scenario that is executed for incoming requests.

+
+
+
DefaultScenario.java
+
+
package org.citrusframework.simulator;
+
+import com.consol.citrus.http.message.HttpMessage;
+import org.citrusframework.simulator.scenario.*;
+import org.springframework.http.HttpStatus;
+
+@Scenario("DEFAULT_SCENARIO")
+public class DefaultScenario extends AbstractSimulatorScenario {
+
+    @Override
+    public void run(ScenarioDesigner designer) {
+        designer.echo("Default scenario executed!");
+
+        designer.send()
+                .message(new HttpMessage("Welcome to the Citrus simulator")
+                .status(HttpStatus.OK));
+    }
+}
+
+
+
+

The default scenario responds with Http 200 OK.

+
+
+

You can now build and run the simulator application with:

+
+
+
Gradle build
+
+
./gradlew build bootRun
+
+
+
+

You will see the application starting up. Usually you will see some console log output. The web server should start within seconds. Once the application is up and running +you can open your browser and point to http://localhost:8080. You will see the very basic simulator user interface. For a more detailed user interface please +follow chapter user-interface.

+
+
+

The REST default scenario that we have added is accessible using http://localhost:8080/services/rest/. You will see the default output Welcome to the Citrus simulator +coming from default scenario execution.

+
+
+

That’s it you are ready to use the Citrus simulator now. Next thing would be to understand the concepts and create some simulator scenarios.

+
+
+
+

2.2. Build with Maven

+
+

You can use the simulator in your Maven project. Maven will automatically download all required dependencies and project artifacts for you. The simulator is then executable +as a web archive representing the the latest simulator distribution.

+
+
+

See the following sample Maven project POM file that defines the project sources as Spring boot application.

+
+
+
+
<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.citrusframework</groupId>
+  <artifactId>citrus-simulator-sample</artifactId>
+  <name>${project.artifactId}</name>
+  <version>1.0.0</version>
+
+  <properties>
+    <java.version>11</java.version>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    <citrus.simulator.version>2.1.1</citrus.simulator.version>
+    <spring.boot.version>2.5.4</spring.boot.version>
+  </properties>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-dependencies</artifactId>
+        <version>${spring.boot.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+
+    <!-- Citrus Simulator -->
+
+    <dependency>
+      <groupId>org.citrusframework</groupId>
+      <artifactId>citrus-simulator-starter</artifactId>
+      <version>${citrus.simulator.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.8.1</version>
+        <configuration>
+          <encoding>${project.build.sourceEncoding}</encoding>
+          <source>${java.version}</source>
+          <target>${java.version}</target>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-war-plugin</artifactId>
+        <version>3.1.0</version>
+        <configuration>
+          <failOnMissingWebXml>false</failOnMissingWebXml>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+        <version>${spring.boot.version}</version>
+        <configuration>
+          <fork>true</fork>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
+
+
+
+

The above Maven pom file will setup the Spring Boot dependencies for you. In addition to that we add the basic citrus-simulator-starter dependency. We are now finished with the build setup and +we are ready to build and run the simulator.

+
+
+

Before we do that let’s add a basic Spring Boot main class to our project.

+
+
+
src/main/java/com/consol/citrus/simulator/Simulator.java
+
+
package org.citrusframework.simulator;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Simulator {
+
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+}
+
+
+
+

Also we add an default scenario that is executed for incoming requests.

+
+
+
DefaultScenario.java
+
+
package org.citrusframework.simulator;
+
+import com.consol.citrus.http.message.HttpMessage;
+import org.citrusframework.simulator.scenario.*;
+import org.springframework.http.HttpStatus;
+
+@Scenario("DEFAULT_SCENARIO")
+public class DefaultScenario extends AbstractSimulatorScenario {
+
+    @Override
+    public void run(ScenarioDesigner designer) {
+        designer.echo("Default scenario executed!");
+
+        designer.send()
+                .message(new HttpMessage("Welcome to the Citrus simulator")
+                .status(HttpStatus.OK));
+    }
+}
+
+
+
+

The default scenario responds with Http 200 OK.

+
+
+

You can now build and run the simulator with Maven from a command line. Use following commands:

+
+
+
Maven build
+
+
mvn clean install spring-boot:run
+
+
+
+

You will see the application starting up. Usually you will see some console log output. The web server should start within seconds. Once the application is up and running +you can open your browser and point to http://localhost:8080. You will see the very basic simulator user interface. For a more detailed user interface please +follow chapter user-interface.

+
+
+

The REST default scenario that we have added is accessible using http://localhost:8080/services/rest/. You will see the default output Welcome to the Citrus simulator +coming from default scenario execution.

+
+
+

That’s it you are ready to use the Citrus simulator now. Next thing would be to understand the concepts and create some simulator scenarios.

+
+
+
+

2.3. Maven archetypes

+
+

The easiest way to get started with a new simulator project is to use a Maven archetype that creates a new project for you.

+
+
+
+
mvn archetype:generate -B -DarchetypeGroupId=org.citrusframework.archetypes \
+                            -DarchetypeArtifactId=citrus-simulator-archetype-rest \
+                            -DarchetypeVersion=${citrus.simulator.version} \
+                            -DgroupId=org.citrusframework.simulator \
+                            -DartifactId=citrus-simulator-rest \
+                            -Dversion=1.0.0
+
+
+
+

If you execute the command above the Maven archetype generator will ask you some questions about versions and project names. Once you have completed the generation +you get a new Maven project that is ready to use. The project is created in a new folder on your machine. Switch to that folder and continue to build the project.

+
+
+

There are different simulator archetypes available. Please pick the most convenient archetype according to your project purpose.

+
+
+
Maven archetypes
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+citrus-simulator-archetype-rest + +

Http REST simulator sample

+
+citrus-simulator-archetype-ws + +

SOAP web service simulator sample

+
+citrus-simulator-archetype-jms + +

JMS simulator sample

+
+citrus-simulator-archetype-mail + +

Mail simulator sample

+
+citrus-simulator-archetype-swagger + +

Auto generate simulator from Swagger Open API specification (experimental)

+
+citrus-simulator-archetype-wsdl + +

Auto generate simulator from SOAP WSDL specification (experimental)

+
+
+
+
+

2.4. Development

+
+

Once the project build and sources are setup you can start to code some simulator scenarios. The project is a normal Java project that you can work with in your +favorite Java IDE. Build tools like Maven and Gradle compile and package the simulator to an executable artifact. Usually this is a Spring boot web archive.

+
+
+

The simulator web application should start within seconds. Once the application is up and running on your local machine +you can open your browser and point to http://localhost:8080.

+
+
+

Now everything is set up and you can start to create some simulator scenarios.

+
+
+

That’s it you are ready to use the Citrus simulator.

+
+
+
+
+
+

3. Concepts

+
+
+

The Citrus simulator’s primary focus is to provide a simple means to simulate one or more endpoints (HTTP, JMS, SMTP, etc). Once the simulator is up and running it waits for an incoming request (JSON, SOAP, XML, etc) to arrive on any of its configured endpoints and reacts accordingly. The simulator examines the incoming request and determines which simulator scenario should be executed for handling the request. Typically the selected scenario creates a response message to send back to the calling client.

+
+
+

A scenario mapper is used to help the simulator determine the correct scenario to be executed. Different scenario mappers are supported that use the request header data or payload data to determine the appropriate scenario to run.

+
+
+

A simulator scenario is capable of handling one or more request messages. When executed it receives the request and can return an appropriate response message to the calling client. +By defining multiple simulator scenarios the citrus simulator is able to respond to different requests accordingly.

+
+
+

A simulator scenario is composed of one or more actions. For the most trivial scenarios there is generally an action for receiving a request and an action for sending a response. +Because the Citrus simulator has access to the underlying Citrus framework functionality you can access a wide range of actions that are available within the Citrus framework and use these when configuring a scenario.

+
+
+

To keep an eye on what the simulator is doing, what requests were received or sent or what scenarios were executed a user interface is provided. In addition to seeing what is going on it is also possible to trigger scenarios manually.

+
+
+

3.1. Simulator application

+
+

The simulator is a usual Spring boot application. This means we have a main class that loads the Spring boot application.

+
+
+
+
import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Simulator {
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+}
+
+
+
+

This class is the main entrance for all configuration and customization statements. As we have added the citrus-simulator-starter as dependency in our project +auto configuration is enabled for the simulator application. By default REST support is enabled with all configuration needed. You can enable/disable the message transport +support for different technologies via application properties.

+
+
+
application.properties
+
+
citrus.simulator.rest.enabled=true
+citrus.simulator.jms.enabled=true
+citrus.simulator.ws.enabled=true
+citrus.simulator.ws.client.enabled=true
+citrus.simulator.endpoint.enabled=true
+
+
+
+

When enabled the simulator auto configuration for this specific message transport is activated and all required beans and configuration is loaded at startup.

+
+
+
Simulator support
+ + + + + + + + + + + + + + + + + + + + + +
+citrus.simulator.rest.enabled + +

Enables Http REST support

+
+citrus.simulator.ws.enabled + +

Enables SOAP web services support

+
+citrus.simulator.ws.client.enabled + +

Enables SOAP web services support

+
+citrus.simulator.jms.enabled + +

Enables JMS support

+
+citrus.simulator.endpoint.enabled + +

Enables generic endpoint component support

+
+
+
+

You can also combine message transports on the simulator application although this feature is of experimental nature up to now.

+
+
+
+

3.2. Simulator properties

+
+

The simulator is capable of loading configuration from system properties, environment variables and property files. First of all the default Spring boot properties configuration mechanism +is supported. Following from that you can add properties to the application.properties file in your project resources in order to adjust simulator behavior.

+
+
+
+
citrus.simulator.rest.enabled=true
+citrus.simulator.defaultTimeout=10000
+citrus.simulator.templatePath=com/company/simulator/templates
+
+
+
+

The available simulator properties are grouped in following configuration classes:

+
+
+ + + + + + + + + + + + + + + + + + + + + +
+org.citrusframework.simulator.config.SimulatorConfigurationProperties + +

prefix=citrus.simulator

+
+org.citrusframework.simulator.http.SimulatorRestConfigurationProperties + +

prefix=citrus.simulator.rest

+
+org.citrusframework.simulator.ws.SimulatorWebServiceConfigurationProperties + +

prefix=citrus.simulator.ws

+
+org.citrusframework.simulator.ws.SimulatorWebServiceClientConfigurationProperties + +

prefix=citrus.simulator.ws.client

+
+org.citrusframework.simulator.jms.SimulatorJmsConfigurationProperties + +

prefix=citrus.simulator.jms

+
+
+
+

There are several properties that you can use in order to customize the simulator behavior. These +properties are:

+
+
+
Spring boot application properties
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+citrus.simulator.templatePath + +

Default path to message payload template files.

+
+citrus.simulator.defaultScenario + +

Default scenario name.

+
+citrus.simulator.defaultTimeout + +

Timeout when waiting for inbound messages.

+
+citrus.simulator.templateValidation + +

Enable/disable schema validation.

+
+citrus.simulator.exceptionDelay + +

Default delay in milliseconds to wait after uncategorized exceptions.

+
+citrus.simulator.rest.urlMapping + +

Handler adapter url mapping for inbound requests

+
+citrus.simulator.ws.servletMapping + +

Message dispatcher servlet mapping for inbound SOAP requests

+
+citrus.simulator.jms.inboundDestination + +

JMS destination name to consume inbound messages from

+
+citrus.simulator.jms.replyDestination + +

JMS destination name to publish reply messages to

+
+
+
+

Please refer to the respective configuration property classes to see what property settings are supported.

+
+
+

3.2.1. System properties

+
+

In addition to that default Spring boot property replacement the simulator also supports system property and environment variables. The properties are:

+
+
+
System property names
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+citrus.simulator.configuration.class + +

Java configuration class that is automatically loaded. (default is org.citrusframework.simulator.SimulatorConfig)

+
+citrus.simulator.template.path + +

Default path to message payload template files.

+
+citrus.simulator.default.scenario + +

Default scenario name.

+
+citrus.simulator.default.timeout + +

Timeout when waiting for inbound messages.

+
+citrus.simulator.template.validation + +

Enable/disable schema validation.

+
+citrus.simulator.exception.delay + +

Default delay in milliseconds to wait after uncategorized exceptions.

+
+citrus.simulator.rest.url.mapping + +

Handler adapter url mapping for inbound requests

+
+citrus.simulator.ws.servlet.mapping + +

Message dispatcher servlet mapping for inbound SOAP requests

+
+citrus.simulator.jms.inbound.destination + +

JMS destination name to consume inbound messages from

+
+citrus.simulator.jms.reply.destination + +

JMS destination name to publish outbound messages to

+
+
+
+

You can set these properties as system properties when starting the Spring boot web application or you can add the properties to the default +Spring Boot application properties file application.properties that is located as resource file in your project.

+
+
+

The simulator will automatically load these properties during startup and honor this configuration.

+
+
+
+

3.2.2. Environment variables

+
+

Same settings that are editable via system properties are also accessible via environment variables. This is extremely helpful when running the simulator in a containerized +infrastructure such as Docker or Kubernetes.

+
+
+
Environment settings
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+CITRUS_SIMULATOR_CONFIGURATION_CLASS + +

Java configuration class that is automatically loaded. (default is org.citrusframework.simulator.SimulatorConfig)

+
+CITRUS_SIMULATOR_TEMPLATE_PATH + +

Default path to message payload template files.

+
+CITRUS_SIMULATOR_DEFAULT_SCENARIO + +

Default scenario name.

+
+CITRUS_SIMULATOR_DEFAULT_TIMEOUT + +

Timeout when waiting for inbound messages.

+
+CITRUS_SIMULATOR_TEMPLATE_VALIDATION + +

Enable/disable schema validation.

+
+CITRUS_SIMULATOR_EXCEPTION_DELAY + +

Default delay in milliseconds to wait after uncategorized exceptions.

+
+CITRUS_SIMULATOR_REST_URL_MAPPING + +

Handler adapter url mapping for inbound requests

+
+CITRUS_SIMULATOR_WS_SERVLET_MAPPING + +

Message dispatcher servlet mapping for inbound SOAP requests

+
+CITRUS_SIMULATOR_JMS_INBOUND_DESTINATION + +

JMS destination name to consume inbound messages from

+
+CITRUS_SIMULATOR_JMS_REPLY_DESTINATION + +

JMS destination name to publish outbound messages to

+
+
+
+

In case these environment variables are present on your local system the simulator will automatically load these settings during startup and honor the configuration.

+
+
+
+
+

3.3. Spring bean configuration

+
+

Citrus works with the Spring framework and the simulator is a Spring boot application. Therefore the configuration is done by adding and overwriting Spring beans in +the application context. The simulator automatically loads Spring beans defined in following locations:

+
+
+
    +
  • +

    META-INF/citrus-simulator.xml Xml Spring bean configuration file.

    +
  • +
  • +

    org.citrusframework.simulator.SimulatorConfig Java configuration class. You can customize this class by defining the property citrus.simulator.configuration.class

    +
  • +
+
+
+

All beans defined in there get automatically loaded to the simulator Spring application context.

+
+
+
+

3.4. Scenario mapper

+
+

The scenario mapper implementation decides how to map incoming request message to simulator scenarios. Each incoming request +triggers a predefined scenario that generates the response message for the calling client. The simulator identifies the scenario based +on a mapping key that is extracted from the incoming request.

+
+
+

There are multiple ways to identify the simulator scenario from incoming request messages:

+
+
+
Scenario mapping identifiers
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Message-Type + +

Each request message type (XML root QName) results in a separate simulator scenario

+
+Content based Xpath + +

Evaluates Xpath expressions on the request payload and uses the expression result as scenario name

+
+Content based JsonPath + +

Evaluates JsonPath expressions on the request payload and uses the expression result as scenario name

+
+Header value + +

Evaluates header name on request and uses header value as scenario name

+
+SOAP action + +

Evaluates SOAP action header on request and uses the value as scenario name

+
+REST request mappings + +

Identifies the scenario based on Http method and resource path on server

+
+Request mapping annotations + +

Uses Spring @RequestMapping annotations on scenarios in order to map incoming requests based on request method and/or request path values

+
+
+
+

Once the simulator scenario is identified with the respective mapping key the scenario get loaded and executed. All scenarios perform Citrus test logic in order +to provide a proper response messages as a result. This way the simulator is able to perform complex response generating logic with dynamic values and so on.

+
+
+

The mentioned mapping key extraction strategies are implemented in these classes:

+
+
+
Scenario mapper implementations
+ + + + + + + + + + + + + + + + + + + + + +
+HttpRequestAnnotationScenarioMapper + +

Evaluates REST request mappings

+
+SoapActionScenarioMapper + +

Evaluates the SOAP action header

+
+HeaderValueScenarioMapper + +

Evaluates any message header

+
+ContentBasedXPathScenarioMapper + +

Evaluates a XPath expression on the message payload

+
+ContentBasedJsonPathScenarioMapper + +

Evaluates a JsonPath expression on the message payload

+
+
+
+

Of course you can also implement a custom scenario mapper, too. Just implement the interface methods of that API and add the implementation to the simulator +configuration as described later on in this document.

+
+
+

3.4.1. Default mapping behavior

+
+

The default mapping key logic extracts the message type of incoming requests. This is done by evaluating a Xpath expression on the request payload that uses the root element of the message as the +mapping key. Each message type gets its own simulator scenario.

+
+
+

Let’s demonstrate that in a simple example. We know three different message types named successMessage, warningMessage and errorMessage. We create a simulator scenario for each of these message types with +respective naming. Given the following incoming requests the simulator will pick the matching scenario for execution.

+
+
+
+
<successMessage>
+    <text>This is a success message</text>
+</successMessage>
+
+<warningMessage>
+    <text>This is a warning message</text>
+</warningMessage>
+
+<errorMessage>
+    <text>This is a error message</text>
+</errorMessage>
+
+
+
+

The simulator evaluates the root element name and maps the requests to the matching scenario. Each scenario implements different response generating logic so the simulator is able to respond to a successMessage in a different +way than for errorMessage types.

+
+
+
+

3.4.2. Custom mapper configuration

+
+

You can change the scenario mapping behavior by overwriting the default scenario mapper in your simulator.

+
+
+
+
@Component
+public class SimulatorAdapter extends SimulatorRestAdapter {
+    @Override
+    public ScenarioMapper scenarioMapper() {
+        HeaderValueScenarioMapper scenarioMapper = new HeaderValueScenarioMapper();
+        scenarioMapper.setHeaderName("X-simulator-scenario");
+        return scenarioMapper;
+    }
+}
+
+
+
+

With the configuration above we use the HeaderValueScenarioMapper implementation so the header name X-simulator-scenario gets evaluated for each incoming request message. +Depending on that header value the matching scenario is executed as a result. The scenario mapper is just a bean in the Spring application context. There is a default implementation but you can overwrite +this behavior very easy in the simulator adapter configuration. Read more about how to add simulator adapter configuration classes in configuration chapters rest-config, ws-config +or jms-config.

+
+
+
+
+

3.5. Simulator scenarios

+
+

The simulator provides the response generating logic by defining one to many scenarios that get executed based on the incoming request. The different scenarios on the simulator +describe different response messages and stand for individual simulation logic. Each scenario is capable of receiving and validating the incoming request message. Based on that the scenario +is in charge of constructing a proper response message.

+
+
+

First of all the scenario gets a name under which mapping strategies can identify the scenario. This name is very important when it comes to mapping incoming requests to scenarios. Besides that +the scenario is a normal Java class that implements following interface SimulatorScenario

+
+
+
+
package org.citrusframework.simulator.scenario;
+
+public interface SimulatorScenario {
+    ScenarioEndpoint getScenarioEndpoint();
+
+    default void run(ScenarioDesigner designer) {}
+
+    default void run(ScenarioRunner runner) {}
+}
+
+
+
+

The simulator scenario provides access to the endpoint that has been invoked to start this scenario. In the scenario logic you can receive that request message using this endpoint. Also you can send back a +response using this scenario endpoint. The scenario logic goes to one of the default run methods depending on what designer or runner Java DSL API you want to use. Fortunately there is an abstract default +implementation org.citrusframework.simulator.scenario.AbstractSimulatorScenario that you can inherit from.

+
+
+

So a typical simulator scenario can look like the following code sample.

+
+
+
+
@Scenario("Hello")
+public class HelloScenario extends AbstractSimulatorScenario {
+
+    @Override
+    public void run(ScenarioDesigner scenario) {
+        scenario
+            .receive()
+            .payload("<Hello xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                        "Say Hello!" +
+                     "</Hello>");
+
+        scenario
+            .send()
+            .payload("<HelloResponse xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                        "Hi there!" +
+                     "</HelloResponse>");
+    }
+}
+
+
+
+

As you can see we extend from AbstractSimulatorScenario. We just add receive and send logic for generating the response message in the designer run method. The scenario above is annotated with @Scenario +for defining a scenario name. There is one single configure method to be implemented. We can use Citrus Java DSL methods in the method body in order to receive the incoming request and send back a proper response message. +Of course we can use the full Citrus power here in order to construct different message payloads such as XML, JSON, PLAINTEXT and so on.

+
+
+

So we could also extract dynamic values from the request in order to reuse those in our response message:

+
+
+
+
@Scenario("Hello")
+public class HelloScenario extends AbstractSimulatorScenario {
+
+    @Override
+    public void run(ScenarioDesigner scenario) {
+        scenario
+            .receive()
+            .payload("<Hello xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                        "<user>@ignore@</user>" +
+                     "</Hello>")
+            .extractFromPayload("/Hello/user", "userName");
+
+        scenario
+            .send()
+            .payload("<HelloResponse xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                        "<text>Hi there ${userName}!</text>" +
+                     "</HelloResponse>");
+    }
+}
+
+
+
+

In the receive operation the user name value is extracted to a test variable ${userName}. In the response we are able to use this variable in order to greet the user by name. This way +we can use the Citrus test power for generating dynamic response messages. Of course this mechanism works for XML, Json and Plaintext payloads.

+
+
+

Now you are ready to write different scenarios that generate different response messages for the calling client. Just remember the scenarios do get unique names that match a value that is evaluated through the scenario mapper. With this +mechanism we are able to code different simulator scenarios for different incoming request messages. Also with the Citrus send and receive operations we are able to handle messages of different transport types.

+
+
+

Read more about special message transport support in chapters rest-support, web-service-support, jms-support, endpoint-support.

+
+
+
+

3.6. Intermediate Messages

+
+

The simulator starts a new scenario instance for each incoming request based on the defined scenario mapping. Sometimes a running scenario instance needs to receive another +incoming request. The simulator has to handle things in a different way then because the incoming request should not trigger an new scenario instance but forward that request to the running +scenario.

+
+
+

This mechanism is called intermediate message handling in scenarios. The scenario can activate the message correlation for a certain request. The simulator will always check all running scenarios +for matching intermediate message correlations before starting a new scenario. This mechanism enables us to receive further incoming request within the same scenario instance.

+
+
+

Let us clarify this with a simple example.

+
+
+
+
@Scenario("GoodNight")
+public class GoodNightScenario extends AbstractSimulatorScenario {
+
+    @Override
+    public void run(ScenarioDesigner scenario) {
+        scenario
+            .receive()
+            .payload("<GoodNight xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                        "Go to sleep!" +
+                     "</GoodNight>")
+            .extractFromHeader("X-CorrelationId", "correlationId");
+
+        scenario.correlation().start()
+            .onHeader("X-CorrelationId", "${correlationId}");
+
+        scenario
+            .send()
+            .payload("<GoodNightResponse xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                        "Good Night!" +
+                    "</GoodNightResponse>");
+
+        scenario
+            .receive()
+            .payload("<IntermediateRequest>In between!</IntermediateRequest>");
+
+        scenario
+            .send()
+            .payload("<IntermediateResponse>In between!</IntermediateResponse>");
+    }
+}
+
+
+
+

The scenario above starts a new correlation on the header entry "X-CorrelationId" and the variable value ${correlationId} that has been extracted from first request message. Now the scenario is able to receive +further incoming request messages with the correlation identifier. This makes sure that no other running scenario is catching this message before. The scenario is provided with that specific message first.

+
+
+

This is how a scenario instance is able to receive more than one single incoming request. The message correlation is able to run on header values as well as based on XPath expressions evaluated within the message payload.

+
+
+
+
@Scenario("FaxCancelled")
+public class FaxCancelledScenario extends AbstractFaxScenario {
+
+    public static final String ROOT_ELEMENT_XPATH = "string:local-name(/*)";
+    public static final String REFERENCE_ID_XPATH = "//fax:referenceId";
+
+    @Override
+    public void run(ScenarioDesigner scenario) {
+        scenario
+            .receive()
+            .xpath(ROOT_ELEMENT_XPATH, "SendFaxMessage")
+            .extractFromPayload(REFERENCE_ID_XPATH, "referenceId");
+
+        scenario.correlation().start()
+                .onPayload(REFERENCE_ID_XPATH, "${referenceId}");
+
+        scenario
+            .send(getStatusEndpoint())
+            .payload(
+                    getPayloadHelper().generateFaxStatusMessage("${referenceId}",
+                            FaxStatusEnumType.QUEUED,
+                            "The fax message has been queued and will be send shortly"),
+                    getPayloadHelper().getMarshaller()
+            );
+
+        scenario
+            .receive()
+            .xpath(ROOT_ELEMENT_XPATH, "CancelFaxMessage")
+            .xpath(REFERENCE_ID_XPATH, "${referenceId}");
+
+        scenario
+            .send(getStatusEndpoint())
+            .payload(
+                    getPayloadHelper().generateFaxStatusMessage("${referenceId}",
+                            FaxStatusEnumType.CANCELLED,
+                            "The fax message has been cancelled"),
+                    getPayloadHelper().getMarshaller()
+            );
+    }
+}
+
+
+
+

The sample above uses XPath expressions to start a new correlation within the scenario. This makes sure that the second incoming request CancelFaxMessage is forwarded to this very same scenario.

+
+
+

With intermediate message handling we are able to create more complex scenarios that require multiple request/response messages. The message correlation makes sure that the +needed requests are handled within the same scenarion and other parallel running scenarios are not affected.

+
+
+
+
+
+

4. REST support

+
+
+

The simulator is able to provide Http REST APIs as a server. Clients can call the simulator on request paths using methods such as +Http GET, POST, PUT, DELETE and so on.

+
+
+

The generic rest support is activated by setting the property citrus.simulator.rest.enabled=true. You can do so in the basic application.properties +file or via system property or environment variable setting.

+
+
+
+
import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Simulator {
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+}
+
+
+
+

The citrus.simulator.rest.enabled property performs some auto configuration steps and loads required beans for the Spring application context +in the Spring boot application.

+
+
+

After that we are ready to handle incoming REST API calls on the simulator.

+
+
+

4.1. Configuration

+
+

Once the REST support is enabled on the simulator we have different configuration options. The most comfortable way is to +add a SimulatorRestAdapter implementation to the classpath. The adapter provides several configuration methods.

+
+
+
+
public abstract class SimulatorRestAdapter implements SimulatorRestConfigurer {
+
+    @Override
+    public ScenarioMapper scenarioMapper() {
+        return new HttpRequestAnnotationScenarioMapper();
+    }
+
+    @Override
+    public HandlerInterceptor[] interceptors() {
+        return new HandlerInterceptor[] { new LoggingHandlerInterceptor() };
+    }
+
+    @Override
+    public String urlMapping() {
+        return "/services/rest/**";
+    }
+}
+
+
+
+

The adapter defines methods that configure the simulator REST handling. For instance we can add another scenario mapper implementation or +add handler interceptors to the REST API call handling.

+
+
+

Note +The REST support is using the default scenario mapper HttpRequestAnnotationScenarioMapper that searches for @RequestMapping annotations +on scenario classes. Read more about that in rest-request-mapping.

+
+
+

The urlMapping defines how clients can access the simulator REST API. Assuming the Spring boot simulator application is running on port 8080 the +REST API would be accessible on this URI:

+
+
+
+
http://localhost:8080/services/rest/*
+
+
+
+

The clients can send GET, POST, DELETE and other calls to that endpoint URI then. The simulator will respond with respective responses based on the called +scenario.

+
+
+

You can simply extend the adapter in a custom class for adding customizations.

+
+
+
+
@Component
+public class MySimulatorRestAdapter extends SimulatorRestAdapter {
+
+    @Override
+    public String urlMapping() {
+        return "/my-rest-service/**";
+    }
+}
+
+
+
+

As you can see the class is annotated with @Component annotation. This is because the adapter should be recognized by Spring in order to overwrite the default +REST adapter behavior. The custom adapter just overwrites the urlMapping method so the REST simulator API will be accessible for clients under this endpoint URI:

+
+
+
+
http://localhost:8080/my-rest-service/*
+
+
+
+

This is the simplest way to customize the simulator REST support. We can also use the adapter extension directly on the Spring boot main application class:

+
+
+
+
import org.citrusframework.simulator.http.SimulatorRestAdapter;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Simulator extends SimulatorRestAdapter {
+
+    @Override
+    public String urlMapping() {
+        return "/my-rest-service/**";
+    }
+
+    @Override
+    public ScenarioMapper scenarioMapper() {
+        HeaderValueScenarioMapper scenarioMapper = new HeaderValueScenarioMapper();
+        scenarioMapper.setHeaderName("X-simulator-scenario");
+        return scenarioMapper;
+    }
+
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+}
+
+
+
+
+

4.2. Advanced customizations

+
+

For a more advanced configuration option we can extend the SimulatorRestSupport implementation.

+
+
+
+
import org.citrusframework.simulator.http.SimulatorRestAutoConfiguration;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Simulator extends SimulatorRestAutoConfiguration {
+
+    @Override
+    protected String getUrlMapping() {
+        return "/my-rest-service/**";
+    }
+
+    @Override
+    public FilterRegistrationBean requestCachingFilter() {
+        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new RequestCachingServletFilter());
+
+        String urlMapping = getUrlMapping();
+        if (urlMapping.endsWith("**")) {
+            urlMapping = urlMapping.substring(0, urlMapping.length() - 1);
+        }
+        filterRegistrationBean.setUrlPatterns(Collections.singleton(urlMapping));
+        return filterRegistrationBean;
+    }
+
+    @Override
+    public HandlerMapping handlerMapping(ApplicationContext applicationContext) {
+        SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
+        handlerMapping.setOrder(Ordered.HIGHEST_PRECEDENCE);
+        handlerMapping.setAlwaysUseFullPath(true);
+
+        Map<String, Object> mappings = new HashMap<>();
+        mappings.put(getUrlMapping(), getRestController(applicationContext));
+
+        handlerMapping.setUrlMap(mappings);
+        handlerMapping.setInterceptors(interceptors());
+
+        return handlerMapping;
+    }
+
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+}
+
+
+
+

With that configuration option we can overwrite REST support auto configuration features on the simulator such as the requestCachingFilter or the handlerMapping. +We extend the SimulatorRestAutoConfiguration implementation directly.

+
+
+
+

4.3. Request mapping

+
+

By default the simulator will map incoming requests to scenarios using so called mapping keys that are evaluated on the incoming request. When using REST support on +the simulator we can also use @RequestMapping annotations on scenarios in order to map incoming requests.

+
+
+

This looks like follows:

+
+
+
+
@Scenario("Hello")
+@RequestMapping(value = "/services/rest/simulator/hello", method = RequestMethod.POST, params = {"user"})
+public class HelloScenario extends AbstractSimulatorScenario {
+
+    @Override
+    public void run(ScenarioDesigner scenario) {
+        scenario
+            .receive()
+            .payload("<Hello xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                        "Say Hello!" +
+                     "</Hello>");
+
+        scenario
+            .send()
+            .payload("<HelloResponse xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                        "Hi there!" +
+                     "</HelloResponse>");
+    }
+}
+
+
+
+

As you can see the example above uses @RequestMapping annotation in addition to the @Scenario annotation. +All requests on the request path /services/rest/simulator/hello of method POST that include the query +parameter user will be mapped to the scenario. With this strategy the simulator is able to map requests based +on methods, request paths and query parameters.

+
+
+

The mapping strategy requires a special scenario mapper implementation that is used by default. This scenario mapper automatically scans for scenarios with @RequestMapping annotations. +The HttpRequestAnnotationScenarioMapper is active by default when enabling REST support on the simulator. Of course you can use traditional scenario mappers, too when using REST. +So in case you need to apply different mapping strategies you can overwrite the scenario mapper implementation in the configuration adapter.

+
+
+
+

4.4. Http responses

+
+

As Http is a synchronous messaging transport by its nature we can provide response messages to the calling client. In Http REST APIs this should include some Http status code. +You can specify the Http status code very easy when using the Citrus Java DSL methods as shown in the next example.

+
+
+
+
@Scenario("Hello")
+@RequestMapping(value = "/services/rest/simulator/hello", method = RequestMethod.POST)
+public class HelloScenario extends AbstractSimulatorScenario {
+
+    @Override
+    public void run(ScenarioDesigner scenario) {
+        scenario
+            .http()
+            .receive()
+            .post()
+            .payload("<Hello xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                    "Say Hello!" +
+                    "</Hello>");
+
+        scenario
+            .http()
+            .send()
+            .response(HttpStatus.OK)
+            .payload("<HelloResponse xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                    "Hi there!" +
+                    "</HelloResponse>");
+    }
+}
+
+
+
+

The Http Java DSL extension in Citrus provides easy access to Http related identities such as request methods, query parameters and status codes. Please +see the official Citrus documentation for more details how to use this Http specific Java fluent API.

+
+
+
+

4.5. Swagger support

+
+

The simulator application is able to read Swagger Open API V3.0 specifications for auto +generating simulator scenarios for each operation. The Open API specification defines available REST request paths, supported methods (GET, POST, PUT, DELETE, …​) and their outcome when clients +call that API operations. The simulator generates basic scenarios for these specification information.

+
+
+

See the following sample how to do that:

+
+
+
+
@SpringBootApplication
+public class Simulator extends SimulatorRestAdapter {
+
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+
+    @Override
+    public ScenarioMapper scenarioMapper() {
+        return new HttpRequestPathScenarioMapper();
+    }
+
+    @Override
+    public String urlMapping(SimulatorRestConfigurationProperties simulatorRestConfiguration) {
+        return "/petstore/v2/**";
+    }
+
+    @Override
+    public EndpointAdapter fallbackEndpointAdapter() {
+        return new StaticEndpointAdapter() {
+            @Override
+            protected Message handleMessageInternal(Message message) {
+                return new HttpMessage().status(HttpStatus.NOT_FOUND);
+            }
+        };
+    }
+
+    @Bean
+    public static HttpScenarioGenerator scenarioGenerator() {
+        HttpScenarioGenerator generator = new HttpScenarioGenerator(new ClassPathResource("swagger/petstore-api.json"));
+        generator.setContextPath("/petstore");
+        return generator;
+    }
+}
+
+
+
+

The listing above adds a HttpScenarioGenerator as Spring bean to the simulator application. The generator receives the swagger api file location swagger/petstore-api.json and the +context path for this API. In addition to that we need to set a special scenario mapper implementation HttpRequestPathScenarioMapper that is aware of generated REST scenarios.

+
+
+

Also we set a custom fallback endpoint adapter. This one is used when no scenario matches the incoming request or when the scenario itself did not produce a proper response because of +some validation error.

+
+
+

On startup the generator dynamically generates a scenario for each operation defined in that swagger api file. You can review all generated scenarios in the user interface.

+
+
+

Let’s have a look at a sample operation in that petstore swagger api file:

+
+
+
+
"/pet/findByStatus": {
+  "get": {
+    "tags": [
+      "pet"
+    ],
+    "summary": "Finds Pets by status",
+    "description": "Multiple status values can be provided with comma separated strings",
+    "operationId": "findPetsByStatus",
+    "produces": [
+      "application/xml",
+      "application/json"
+    ],
+    "parameters": [
+      {
+        "name": "status",
+        "in": "query",
+        "description": "Status values that need to be considered for filter",
+        "required": true,
+        "type": "array",
+        "items": {
+          "type": "string",
+          "enum": [
+            "available",
+            "pending",
+            "sold"
+          ],
+          "default": "available"
+        },
+        "collectionFormat": "multi"
+      }
+    ],
+    "responses": {
+      "200": {
+        "description": "successful operation",
+        "schema": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/Pet"
+          }
+        }
+      },
+      "400": {
+        "description": "Invalid status value"
+      }
+    },
+    "security": [
+      {
+        "petstore_auth": [
+          "write:pets",
+          "read:pets"
+        ]
+      }
+    ]
+  }
+}
+
+
+
+

The REST operation above defines a GET method on /pet/findByStatus. The required query parameter status is defined to filter the returned list of pets. As a response +the API defines 200 OK with an array of Pet objects. In addition to that 400 response is defined when the status parameter is not within its restriction enumeration available, pending, sold.

+
+
+

IMPORTANT: The simulator will always generate the success case exclusively. Here this would be the 200 OK response. Other response variations are not generated up to now!

+
+
+

The generated scenario for this operation verifies that the request is using GET method on request path /pet/findByStatus. Also the scenario verifies the existence of the status query +parameter and that the value is within the enumeration boundaries.

+
+
+

Only in case these verification steps are performed successfully the simulator scenario generates a proper response 200 OK that contains a dynamic array of pet objects.

+
+
+

Let’s have a look at the communication on that scenario:

+
+
+
Request
+
+
GET http://localhost:8080/petstore/v2/pet/findByStatus?status=pending
+Accept:application/json
+Content-Type:text/plain;charset=UTF-8
+Content-Length:0
+
+
+
+
Response
+
+
HTTP/1.1 200
+X-Application-Context:application
+Content-Type:application/json
+Content-Length:193
+Date:Wed, 13 Sep 2017 08:13:52 GMT
+
+[{"id": 5243024128,"category": {"id": 5032916791,"name": "hneBENfFDq"},"name": "JjZhcsvSRA","photoUrls": ["GwSVIBOhsi"],"tags": [{"id": 8074462757,"name": "DYwotNekKc"}],"status": "available"}]
+
+
+
+

The request did match all verification steps on the simulator for this operation. Following from that we receive a generated response message with some sample data as array of pet objects. The simulator +is able to generate dynamic identifier such as id, category and name values. According to the field type the simulator generates dynamic number of string values. When there is a enumeration value restriction as seen in status +the simulator generates a dynamic enumeration value.

+
+
+

This is how we always get a proper generated response from the simulator API. The petstore swagger Open API specification defines the returned objects and how to validate the incoming requests.

+
+
+

Just in case we sent an invalid request according to the Open API specification we do not get a proper response.

+
+
+
Request
+
+
GET http://localhost:8080/petstore/v2/pet/findByStatus
+Accept:application/json
+Content-Type:text/plain;charset=UTF-8
+Content-Length:0
+
+
+
+
Response
+
+
HTTP/1.1 404
+X-Application-Context:application
+Content-Type:text/plain;charset=UTF-8
+Content-Length:0
+Date:Wed, 13 Sep 2017 08:42:56 GMT
+
+
+
+

The sample request above is missing the required status query parameter on the findByStatus operation. As a result we get a 404 NOT_FOUND response from the fallback endpoint adapter +as the scenario did not complete because of validation errors. You will see the failed scenario activity with proper error message on that missing status parameter in the user interface then.

+
+
+

Swagger system properties

+
+

The simulator Swagger API auto generate scenario feature can also be activated using pure property settings on the Spring boot application. Instead of adding the Spring bean HttpScenarioGenerator in your +simulator configuration you could just set the following properties on the simulator application:

+
+
+
+
# Enable swagger api support
+citrus.simulator.rest.swagger.enabled=true
+citrus.simulator.rest.swagger.api=classpath:swagger/petstore-api.json
+citrus.simulator.rest.swagger.contextPath=/petstore
+
+
+
+

Of course you can also use environment variables.

+
+
+
+
CITRUS_SIMULATOR_REST_SWAGGER_ENABLED=true
+CITRUS_SIMULATOR_REST_SWAGGER_API=classpath:swagger/petstore-api.json
+CITRUS_SIMULATOR_REST_SWAGGER_CONTEXT_PATH=/petstore
+
+
+
+

We just add the api file location and everything else is auto configuration done in the simulator application.

+
+
+
+

Data dictionaries

+
+

Data dictionaries enable us to centralize data manipulation via JsonPath expressions in order to have more dynamic message values in generated request and response message. +When a scenario receives and sends messages the data dictionary is asked for available translations for message elements. This means that +data dictionaries are able to manipulate message content before they are processed.

+
+
+

The auto generated scenario references both inbound and outbound data dictionaries. We simply need to enable those in the Spring boot application.properties file:

+
+
+
+
citrus.simulator.inbound.json.dictionary.enabled=true
+citrus.simulator.inboundJsonDictionary=classpath:dictionary/inbound_mappings.properties
+citrus.simulator.outbound.json.dictionary.enabled=true
+citrus.simulator.outboundJsonDictionary=classpath:dictionary/outbound_mappings.properties
+
+
+
+

As you can see you have the possibility to define mapping files that map JsonPath expression evaluation with pre defined values in the dictionary:

+
+
+

Now we have added some mapping files for inbound and outbound dictionaries. The mapping file can look like this:

+
+
+
inbound mappings
+
+
$.category.name=@assertThat(anyOf(is(dog),is(cat)))@
+$.status=@matches(available|pending|sold|placed)@
+$.quantity=@greaterThan(0)@
+
+
+
+
outbound mappings
+
+
$.category.name=citrus:randomEnumValue('dog', 'cat')
+$.name=citrus:randomEnumValue('hasso', 'cutie', 'fluffy')
+
+
+
+

The inbound and outbound mapping files defines several JsonPath expressions that should set predefined values before incoming and outgoing messages are validated respectively sent out. +As you can see we can use Citrus validation matchers and functions in order to get more complex value generation.

+
+
+
+
+
+
+

5. Web Service support

+
+
+

The simulator is able to handle SOAP Web Service calls as a server.

+
+
+

The generic SOAP web service support is activated by setting the property citrus.simulator.ws.enabled=true. You can do so in the basic application.properties +file or via system property or environment variable setting.

+
+
+
+
import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Simulator {
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+}
+
+
+
+

The citrus.simulator.ws.enabled property performs some auto configuration steps and loads required beans for the Spring application context +in the Spring boot application.

+
+
+

As SOAP web service support is not included by default in the simulator we need to add some Citrus dependencies to our project. In Maven we simply add the following dependency to the project POM.

+
+
+
+
<!-- Citrus web service support -->
+<dependency>
+  <groupId>com.consol.citrus</groupId>
+  <artifactId>citrus-ws</artifactId>
+  <version>${citrus.version}</version>
+</dependency>
+
+
+
+

After that we are ready to handle incoming SOAP Web Service calls on the simulator. When SOAP web service handling is enabled on the simulator +the SOAP envelope handling is done automatically. This means we do not have to deal with that SOAP envelope in the scenario receive and send operations. Also +the scenario receive operation has access to the SOAP action of the incoming request call. Besides that we can also return a SOAP fault message as scenario outcome.

+
+
+

Let’s move on with having a look at the SOAP related configuration options as described in the following sections.

+
+
+

5.1. Configuration

+
+

Once the SOAP support is enabled on the simulator we have different configuration options. The most comfortable way is to +add a SimulatorWebServiceAdapter implementation to the classpath. The adapter provides several configuration methods.

+
+
+
+
public abstract class SimulatorWebServiceAdapter implements SimulatorWebServiceConfigurer {
+    @Override
+    public String servletMapping() {
+        return "/services/ws/*";
+    }
+
+    @Override
+    public ScenarioMapper scenarioMapper() {
+        return new ContentBasedXPathScenarioMapper().addXPathExpression("local-name(/*)");
+    }
+
+    @Override
+    public EndpointInterceptor[] interceptors() {
+        return new EndpointInterceptor[] { new LoggingEndpointInterceptor() };
+    }
+}
+
+
+
+

The adapter defines methods that configure the simulator SOAP message handling. For instance we can add another mapping key extractor implementation or +add endpoint interceptors to the SOAP service call handling.

+
+
+

The servletMapping defines how clients can access the simulator SOAP service. Assuming the Spring boot simulator application is running on port 8080 the +SOAP service would be accessible on this URI:

+
+
+
+
http://localhost:8080/services/ws/*
+
+
+
+

The clients can send SOAP calls to that endpoint URI then. The simulator will respond with respective SOAP responses based on the called +scenario.

+
+
+

You can simply extend the adapter in a custom class for adding customizations.

+
+
+
+
@Component
+public class MySimulatorWebServiceAdapter extends SimulatorWebServiceAdapter {
+
+    @Override
+    public String servletMapping() {
+        return "/my-soap-service/**";
+    }
+}
+
+
+
+

As you can see the class is annotated with @Component annotation. This is because the adapter should be recognized by Spring in order to overwrite the default +SOAP adapter behavior. The custom adapter just overwrites the servletMapping method so the SOAP simulator API will be accessible for clients under this endpoint URI:

+
+
+
+
http://localhost:8080/my-soap-service/*
+
+
+
+

This is the simplest way to customize the simulator SOAP support. We can also use the adapter extension directly on the Spring boot main application class:

+
+
+
+
import org.citrusframework.simulator.ws.SimulatorWebServiceAdapter;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Simulator extends SimulatorWebServiceAdapter {
+
+    @Override
+    public String servletMapping() {
+        return "/my-soap-service/**";
+    }
+
+    @Override
+    public ScenarioMapper scenarioMapper() {
+        return new SoapActionScenarioMapper();
+    }
+
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+}
+
+
+
+
+

5.2. Advanced customizations

+
+

For a more advanced configuration option we can extend the SimulatorWebServiceSupport implementation.

+
+
+
+
import org.citrusframework.simulator.ws.SimulatorWebServiceAutoConfiguration;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Simulator extends SimulatorWebServiceAutoConfiguration {
+
+    @Override
+    protected String getServletMapping() {
+        return "/my-soap-service/**";
+    }
+
+    @Bean
+    public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
+        MessageDispatcherServlet servlet = new MessageDispatcherServlet();
+        servlet.setApplicationContext(applicationContext);
+        servlet.setTransformWsdlLocations(true);
+        return new ServletRegistrationBean(servlet, getDispatcherServletMapping());
+    }
+
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+}
+
+
+
+

With that configuration option we can overwrite SOAP support auto configuration features on the simulator such as the messageDispatcherServlet. +We extend the SimulatorWebServiceAutoConfiguration implementation directly.

+
+
+
+

5.3. SOAP response

+
+

When using Http SOAP services we may want to respond to the calling client with a synchronous SOAP response message. As the SOAP communication is automatically handled +within the simulator we can simply send back a response message in the scenario.

+
+
+
+
@Scenario("Hello")
+public class HelloScenario extends AbstractSimulatorScenario {
+
+    @Override
+    public void run(ScenarioDesigner scenario) {
+        scenario
+            .soap()
+            .receive()
+            .payload("<Hello xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                        "Say Hello!" +
+                     "</Hello>")
+            .soapAction("Hello");
+
+        scenario
+            .soap()
+            .send()
+            .payload("<HelloResponse xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                        "Hi there!" +
+                     "</HelloResponse>");
+    }
+}
+
+
+
+

As you can see the Citrus Java DSL provides special SOAP related methods that specify the SOAP request and response data. Once again the SOAP envelope is automatically handled +so we do not have to add this here. The receive operation is able to verify the SOAP action header value. In addition to that we are able to specify the synchronous SOAP response message.

+
+
+

When using SOAP message protocols we may need to send SOAP faults as response message. This is handled in the next section.

+
+
+
+

5.4. SOAP faults

+
+

The simulator is in charge of sending proper response messages to the calling client. When using SOAP we might also want to send +back a SOAP fault message. Therefore the default Web Service scenario implementation also provides fault responses as scenario result.

+
+
+
+
@Scenario("GoodNight")
+public class GoodNightScenario extends AbstractSimulatorScenario {
+
+    @Override
+    protected void configure() {
+        scenario
+            .receive()
+            .payload("<GoodNight xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                        "Go to sleep!" +
+                     "</GoodNight>")
+            .header(SoapMessageHeaders.SOAP_ACTION, "GoodNight");
+
+        scenario
+            .sendFault()
+            .faultCode("{http://citrusframework.org}CITRUS:SIM-1001")
+            .faultString("No sleep for me!");
+    }
+}
+
+
+
+

The example above shows a simple fault generating SOAP scenario. The base class SimulatorWebServiceScenario provides +the sendFault() method in order to create proper SOAP fault messages. The simulator automatically add SOAP envelope and SOAP fault +message details for you. So we can decide wheather to provide a success response or SOAP fault.

+
+
+
+

5.5. WSDL support

+
+

The simulator is able to read your WSDL web service specifications for auto generating simulator scenarios. The WSDL defines multiple operations with request and response message data. +The simulator reads the WSDL information and generates basic scenarios for these operations.

+
+
+

See the following sample how to do that:

+
+
+
+
@SpringBootApplication
+public class Simulator extends SimulatorWebServiceAdapter {
+
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+
+    @Override
+    public String servletMapping(SimulatorWebServiceConfigurationProperties simulatorWebServiceConfiguration) {
+        return "/services/ws/HelloService/v1/*";
+    }
+
+    @Override
+    public EndpointAdapter fallbackEndpointAdapter() {
+        return new StaticEndpointAdapter() {
+            @Override
+            protected Message handleMessageInternal(Message message) {
+                return new SoapFault()
+                        .faultActor("SERVER")
+                        .faultCode("{http://localhost:8080/HelloService/v1}HELLO:ERROR-1001")
+                        .faultString("Internal server error");
+            }
+        };
+    }
+
+    @Bean
+    public static WsdlScenarioGenerator scenarioGenerator() {
+        WsdlScenarioGenerator generator = new WsdlScenarioGenerator(new ClassPathResource("xsd/Hello.wsdl"));
+        return generator;
+    }
+}
+
+
+
+

The listing above uses a WsdlScenarioGenerator as Spring bean. The generator requires the WSDL file location xsd/Hello.wsdl and the +servlet mapping path for this API.

+
+
+

Also we set a custom fallback endpoint adapter. This one is used when no scenario matches the incoming request or when the scenario itself did not produce a proper response because of +some validation error. The fallback endpoint adapter sends a default SOAP fault message with "Internal server error".

+
+
+

On startup the generator dynamically generates a scenario for each operation defined in that WSDL file. You can review all generated scenarios in the user interface.

+
+
+

Let’s have a look at the sample WSDL file:

+
+
+
+
<?xml version="1.0" encoding="UTF-8"?>
+<wsdl:definitions name="Hello"
+                  xmlns:tns="http://citrusframework.org/schemas/hello"
+                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                  targetNamespace="http://citrusframework.org/schemas/hello"
+                  xmlns="http://www.w3.org/2001/XMLSchema">
+
+  <wsdl:documentation>Version 1.0</wsdl:documentation>
+
+  <wsdl:types>
+    <xs:schema xmlns="http://citrusframework.org/schemas/hello"
+               xmlns:xs="http://www.w3.org/2001/XMLSchema"
+               targetNamespace="http://citrusframework.org/schemas/hello"
+               version="1.0"
+               elementFormDefault="qualified"
+               attributeFormDefault="unqualified">
+
+      <xs:element name="Hello" type="xs:string"/>
+      <xs:element name="HelloResponse" type="xs:string"/>
+
+      <xs:element name="GoodBye" type="xs:string"/>
+      <xs:element name="GoodByeResponse" type="xs:string"/>
+
+      <xs:element name="GoodNight" type="xs:string"/>
+      <xs:element name="GoodNightResponse" type="xs:string"/>
+    </xs:schema>
+  </wsdl:types>
+
+  <wsdl:message name="Hello">
+    <wsdl:part name="parameters" element="tns:Hello"/>
+  </wsdl:message>
+
+  <wsdl:message name="HelloResponse">
+    <wsdl:part name="parameters" element="tns:HelloResponse"/>
+  </wsdl:message>
+
+  <wsdl:message name="GoodBye">
+    <wsdl:part name="parameters" element="tns:GoodBye"/>
+  </wsdl:message>
+
+  <wsdl:message name="GoodByeResponse">
+    <wsdl:part name="parameters" element="tns:GoodByeResponse"/>
+  </wsdl:message>
+
+  <wsdl:message name="GoodNight">
+    <wsdl:part name="parameters" element="tns:GoodNight"/>
+  </wsdl:message>
+
+  <wsdl:message name="GoodNightResponse">
+    <wsdl:part name="parameters" element="tns:GoodNightResponse"/>
+  </wsdl:message>
+
+  <wsdl:portType name="HelloPortType">
+    <wsdl:operation name="hello">
+      <wsdl:input name="Hello" message="tns:Hello"/>
+      <wsdl:output name="HelloResponse" message="tns:HelloResponse"/>
+    </wsdl:operation>
+    <wsdl:operation name="goodbye">
+      <wsdl:input name="GoodBye" message="tns:GoodBye"/>
+      <wsdl:output name="GoodByeResponse" message="tns:GoodByeResponse"/>
+    </wsdl:operation>
+    <wsdl:operation name="goodnight">
+      <wsdl:input name="GoodNight" message="tns:GoodNight"/>
+      <wsdl:output name="GoodNightResponse" message="tns:GoodNightResponse"/>
+    </wsdl:operation>
+  </wsdl:portType>
+
+  <wsdl:service name="HelloService">
+    <wsdl:port name="HelloServiceHTTP"
+               binding="tns:HelloServiceHTTPBinding">
+      <soap:address location="http://localhost:8080/services/ws/HelloService/v1"/>
+    </wsdl:port>
+  </wsdl:service>
+
+  <wsdl:binding name="HelloServiceHTTPBinding"
+                type="tns:HelloPortType">
+    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+    <wsdl:operation name="hello">
+      <soap:operation style="document"
+                      soapAction="Hello"/>
+      <wsdl:input name="Hello">
+        <soap:body use="literal"/>
+      </wsdl:input>
+      <wsdl:output name="HelloResponse">
+        <soap:body use="literal"/>
+      </wsdl:output>
+    </wsdl:operation>
+    <wsdl:operation name="goodbye">
+      <soap:operation style="document"
+                      soapAction="GoodBye"/>
+      <wsdl:input name="GoodBye">
+        <soap:body use="literal"/>
+      </wsdl:input>
+      <wsdl:output name="GoodByeResponse">
+        <soap:body use="literal"/>
+      </wsdl:output>
+    </wsdl:operation>
+    <wsdl:operation name="goodnight">
+      <soap:operation style="document"
+                      soapAction="GoodNight"/>
+      <wsdl:input name="GoodNight">
+        <soap:body use="literal"/>
+      </wsdl:input>
+      <wsdl:output name="GoodNightResponse">
+        <soap:body use="literal"/>
+      </wsdl:output>
+    </wsdl:operation>
+  </wsdl:binding>
+
+</wsdl:definitions>
+
+
+
+

The WSDL above defines a hello operation with Hello as input and HelloResponse as output. The SOAP action is defined as Hello.

+
+
+

The generated scenario for this operation verifies that the request is a valid Hello request according to the XSD schema definition in the WSDL. Also the scenario verifies the basic XML structure of that message.

+
+
+

Only in case these verification steps are performed successfully the simulator scenario generates a proper response HelloResponse. The generated scenario is able to create dynamic values in the response according to the XSD schema in the WSDL. We will cover this feature in +more detail on later in this chapter.

+
+
+

Let’s have a look at the communication that the generated scenario is going to perform:

+
+
+
Request
+
+
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+    <SOAP-ENV:Header/>
+    <SOAP-ENV:Body>
+        <Hello xmlns="http://citrusframework.org/schemas/hello">Say Hello!</Hello>
+    </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+
+
+
+
Response
+
+
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+    <SOAP-ENV:Header/>
+    <SOAP-ENV:Body>
+        <HelloResponse xmlns="http://citrusframework.org/schemas/hello">GL29HT</hel:HelloResponse>
+    </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+
+
+
+

The Hello SOAP request matches all verification steps on the simulator for this operation. Following from that we receive a generated HelloResponse response message with some sample data. The simulator +is able to generate dynamic values such as GL29HT which is according to the WSDL schema rules a string value.

+
+
+

This is how we always get a proper generated response from the simulator API. The HelloService WSDL specification defines the returned objects and how to validate the incoming requests.

+
+
+

Just in case we sent an invalid request to the simulator we do not get a proper response. For instance if we sent a wrong SOAP action we receive following fault response:

+
+
+
Fault response
+
+
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+    <SOAP-ENV:Header/>
+    <SOAP-ENV:Body>
+        <SOAP-ENV:Fault>
+            <faultcode xmlns:HELLO="http://localhost:8080/HelloService/v1">HELLO:ERROR-1001</faultcode>
+            <faultstring xmlns:xml="http://www.w3.org/XML/1998/namespace" xml:lang="en">Internal server error</faultstring>
+            <faultactor>SERVER</faultactor>
+        </SOAP-ENV:Fault>
+    </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+
+
+
+

As a result we get a SOAP fault message with fault code ERROR-1001 and message "Internal server error" as defined in the fallback endpoint adapter. +You will also see the failed scenario activity with proper error message in the user interface then.

+
+
+

WSDL system properties

+
+

The simulator WSDL auto generate scenario feature can also be activated using pure property settings on the Spring boot application. Instead of adding the Spring bean WsdlScenarioGenerator in your +simulator configuration you could just set the following properties on the simulator application:

+
+
+
+
# Enable SOAP web service support
+citrus.simulator.ws.wsdl.enabled=true
+citrus.simulator.ws.wsdl.location=classpath:xsd/Hello.wsdl
+
+
+
+

Of course you can also use environment variables.

+
+
+
+
CITRUS_SIMULATOR_WS_WSDL_ENABLED=true
+CITRUS_SIMULATOR_WS_WSDL_LOCATION=classpath:xsd/Hello.wsdl
+
+
+
+

We just add the WSDL location and everything else is auto configuration done in the simulator application.

+
+
+
+

Data dictionaries

+
+

The auto generated WSDL scenarios make us of so called data dictionaries in order to create dynamic values both in request and response messages. The data dictionaries +are a well known Citrus functionality that enable us to centralize data manipulation via XPath expressions for example. Each XML message construction will consult the data dictionary +for some translation of elements and attributes.

+
+
+

The auto generated scenario references both inbound and outbound data dictionaries. We simply need to enable those in the Spring boot application.properties file:

+
+
+
+
citrus.simulator.inbound.xml.dictionary.enabled=true
+citrus.simulator.outbound.xml.dictionary.enabled=true
+
+
+
+

These property settings automatically activate the data dictionaries and you will get random numbers and strings in all generated WSDL messages. For incoming requests the dictionary makes sure +that elements and attributes are ignored in validation by default. This is a good idea as we can not know all data that is sent to the simulator.

+
+
+

Fortunately you have the possibility to define mapping files that map XPath expression evaluation with pre defined values in the dictionary:

+
+
+
+
citrus.simulator.inbound.xml.dictionary.enabled=true
+citrus.simulator.inboundXmlDictionary=classpath:dictionary/inbound_mappings.xml
+citrus.simulator.outbound.xml.dictionary.enabled=true
+citrus.simulator.outboundXmlDictionary=classpath:dictionary/outbound_mappings.xml
+
+
+
+

Now we have added some mapping files for inbound and outbound dictionaries. The mapping file can look like this:

+
+
+
+
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<properties>
+  <entry key="//sim:Hello">Say Hello!</entry>
+  <entry key="//sim:GoodBye">Say GoodBye!</entry>
+</properties>
+
+
+
+

The inbound mapping file defines two XPath expressions that should set predefined values before incoming request are validated. So in this case we set Say Hello! as string element value +to the element <Hello> in the request. When dealing with XML and XPath we need to take care on proper namespace handling. In the XPath expression above we make use of the namespace prefix sim:. This prefix resoves to a +proper namespace in the WSDL schema for Hello messages and is defined in a global namespace context within the Spring application.

+
+
+

You can add that namespace context as Spring bean for instance.

+
+
+
+
@Bean
+public NamespaceContextBuilder namespaceContextBuilder() {
+    NamespaceContextBuilder namespaceContextBuilder = new NamespaceContextBuilder();
+    namespaceContextBuilder.getNamespaceMappings().put("sim", "http://citrusframework.org/schemas/hello");
+
+    return namespaceContextBuilder;
+}
+
+
+
+

After that we are able to use the global sim namespace prefix in all XPath expressions. The XPath expression evaluation will take care on proper namespace handling then.

+
+
+

Of course we can also add outbound bindings for creating special response element values.

+
+
+
+
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<properties>
+  <entry key="//sim:HelloResponse">Hello!</entry>
+  <entry key="//sim:GoodByeResponse">GoodBye!</entry>
+</properties>
+
+
+
+

Now the auto generated response for HelloResponse messages will always use Hello! as value. in combination with Citrus functions we are able to define more complex response element values in auto generated messages.

+
+
+
+
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<properties>
+  <entry key="//sim:HelloResponse">citrus:randomString(10)</entry>
+  <entry key="//sim:GoodByeResponse">citrus:randomEnumValue('GoodBye!', 'SeeYaLater!', 'ByeBye!')</entry>
+</properties>
+
+
+
+
+
+
+
+

6. JMS support

+
+
+

The simulator is able to receive messages from message brokers using the Java Message Service API (JMS). As a consumer the simulator constantly polls JMS destinations (queue or topic) +for incoming request messages. When the queue is of synchronous nature the simulator is able to send synchronous response messages.

+
+
+

The generic jms support is activated by setting the property citrus.simulator.jms.enabled=true. You can do so in the basic application.properties +file or via system property or environment variable setting.

+
+
+
+
import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Simulator {
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+}
+
+
+
+

The citrus.simulator.jms.enabled property performs some auto configuration steps and loads required beans for the Spring application context +in the Spring boot application.

+
+
+

As JMS support is not included by default in the simulator we need to add some Citrus dependencies to our project. In Maven we simply add the following dependency to the project POM.

+
+
+
+
<!-- Citrus web service support -->
+<dependency>
+  <groupId>com.consol.citrus</groupId>
+  <artifactId>citrus-jms</artifactId>
+  <version>${citrus.version}</version>
+</dependency>
+
+
+
+

Also we might want to add JMS vendor specific connection factory implementations depending on what JMS message broker you are using:

+
+
+
+
<!-- ActiveMQ message broker support -->
+<dependency>
+  <groupId>org.apache.activemq</groupId>
+  <artifactId>activemq-broker</artifactId>
+  <version>${activemq.version}</version>
+</dependency>
+<dependency>
+  <groupId>org.apache.activemq</groupId>
+  <artifactId>activemq-spring</artifactId>
+  <version>${activemq.version}</version>
+</dependency>
+<dependency>
+  <groupId>org.apache.xbean</groupId>
+  <artifactId>xbean-spring</artifactId>
+  <version>4.4</version>
+</dependency>
+
+
+
+

With that piece of configuration we are ready to handle incoming JMS messages on the simulator. Of course we need a JMS connection factory and other JMS related +configuration options as described in the following sections.

+
+
+

6.1. Configuration

+
+

Once the JMS support is enabled on the simulator we have different configuration options. The most comfortable way is to +add a SimulatorJmsAdapter implementation to the project. The adapter provides several configuration methods.

+
+
+
+
public abstract class SimulatorJmsAdapter implements SimulatorJmsConfigurer {
+    @Override
+    public ConnectionFactory connectionFactory() {
+        return new SingleConnectionFactory();
+    }
+
+    @Override
+    public String destinationName() {
+        return System.getProperty("citrus.simulator.jms.destination", "Citrus.Simulator.Inbound");
+    }
+
+    @Override
+    public boolean useSoapEnvelope() {
+        return false;
+    }
+
+    @Override
+    public ScenarioMapper scenarioMapper() {
+        return new ContentBasedXPathScenarioMapper().addXPathExpression("local-name(/*)");
+    }
+}
+
+
+
+

The adapter defines methods that configure the simulator JMS handling. For instance we can add another scenario mapper implementation or +enable automatic SOAP envelope handling.

+
+
+

The destinationName defines the incoming JMS destination to poll as a consumer. The connectionFactory is mandatory in order to connect to a JMS +message broker.

+
+
+

You can simply extend the adapter in a custom class for adding customizations.

+
+
+
+
@Component
+public class MySimulatorJmsAdapter extends SimulatorJmsAdapter {
+
+    @Override
+    public String destinationName() {
+        return "JMS.Queue.simulator.inbound";
+    }
+
+    @Override
+    public ConnectionFactory connectionFactory() {
+        return new ActiveMQConnectionFactory("tcp://localhost:61616");
+    }
+}
+
+
+
+

As you can see the class is annotated with @Component annotation. This is because the adapter should be recognized by Spring in order to overwrite the default +JMS adapter behavior. The custom adapter just overwrites the connectionFactory and destinationName methods so the JMS simulator will connect to the ActiveMQ message broker +and listen for incoming requests on that queue JMS.Queue.simulator.inbound.

+
+
+

This is the simplest way to customize the simulator JMS support. We can also use the adapter extension directly on the Spring boot main application class:

+
+
+
+
import org.citrusframework.simulator.jms.SimulatorJmsAdapter;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Simulator extends SimulatorJmsAdapter {
+
+    @Override
+    public String destinationName() {
+        return "JMS.Queue.simulator.inbound";
+    }
+
+    @Override
+    public ConnectionFactory connectionFactory() {
+        return new ActiveMQConnectionFactory("tcp://localhost:61616");
+    }
+
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+}
+
+
+
+

Once this is done the simulator polls the target destination for new incoming request messages. As usual the simulator maps incoming requests to simulator scenarios for execution. Inside +the scenario you can receive the JMS request messages as usual using the scenario endpoint. The scenario logic is then able to provide a response message that is sent back to the synchronous reply +destination if any is specified.

+
+
+
+

6.2. Asynchronous communication

+
+

For asynchronous communication we can define normal JMS destination endpoints in Citrus in order to autowire those in the scenario.

+
+
+
+
import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Simulator {
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+
+    @Override
+    public String destinationName() {
+        return "JMS.Queue.simulator.inbound";
+    }
+
+    @Override
+    public ConnectionFactory connectionFactory() {
+        return new ActiveMQConnectionFactory("tcp://localhost:61616");
+    }
+
+    @Bean
+    public JmsEndpoint replyEndpoint() {
+        return CitrusEndpoints.jms()
+                              .asynchronous()
+                              .destinationName("JMS.Queue.simulator.reply")
+                              .connectionFactory(connectionFactory())
+                              .build();
+    }
+}
+
+
+
+

As you can see we are able to create several JMS endpoints in Citrus. We can autowire those endpoints in a scenario for sending back an asynchronous response.

+
+
+
+
@Scenario("Hello")
+public class HelloJmsScenario extends AbstractSimulatorScenario {
+
+    @Autowired
+    private JmsEndpoint replyEndpoint;
+
+    @Override
+    public void run(ScenarioDesigner scenario) {
+        scenario
+            .receive()
+            .payload("<Hello xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                        "<user>@ignore@</user>" +
+                     "</Hello>")
+            .extractFromPayload("/Hello/user", "userName");
+
+        scenario
+            .send(replyEndpoint)
+            .payload("<HelloResponse xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                        "<text>Hi there ${userName}!</text>" +
+                     "</HelloResponse>");
+    }
+}
+
+
+
+

In this sample above we receive the scenario request message as usual using the scenario endpoint. After that we send back a response on the replyEndpoint which has been +injected to the scenario using the @Autowired annotation.

+
+
+

In general scenarios can interact with all Citrus endpoints that get defined as Spring beans in the application context configuration.

+
+
+
+

6.3. Synchronous communication

+
+

When dealing with synchronous communication the message producer waits for a reply message on a reply destination. This reply destination handling is automatically done +within the simulator. So when we have synchronous communication we simply send back a response message using the scenario endpoint. The simulator makes sure that the response is +provided to the waiting producer on the reply destination.

+
+
+
+
@Scenario("Hello")
+public class HelloJmsScenario extends AbstractSimulatorScenario {
+
+    @Override
+    public void run(ScenarioDesigner scenario) {
+        scenario
+            .receive()
+            .payload("<Hello xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                        "<user>@ignore@</user>" +
+                     "</Hello>")
+            .extractFromPayload("/Hello/user", "userName");
+
+        scenario
+            .send()
+            .payload("<HelloResponse xmlns=\"http://citrusframework.org/schemas/hello\">" +
+                        "<text>Hi there ${userName}!</text>" +
+                     "</HelloResponse>");
+    }
+}
+
+
+
+

The synchronous JMS communication needs to be enabled on the JMS simulator adapter.

+
+
+
+
import org.citrusframework.simulator.jms.SimulatorJmsAdapter;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Simulator extends SimulatorJmsAdapter {
+
+    @Override
+    public String destinationName() {
+        return "JMS.Queue.simulator.inbound";
+    }
+
+    @Override
+    public ConnectionFactory connectionFactory() {
+        return new ActiveMQConnectionFactory("tcp://localhost:61616");
+    }
+
+    @Override
+    public boolean synchronous(SimulatorJmsConfigurationProperties simulatorJmsConfiguration) {
+        return true;
+    }
+
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+}
+
+
+
+

This completes the JMS communication support within the simulator. Read more about other messaging transports such as Http REST or SOAP in this documentation.

+
+
+
+
+
+

7. Endpoint support

+
+
+

We have seen how the simulator handles different transports such as Http REST, SOAP web services and JMS. +Now the simulator is also able to handle other message transports such as mail communication, JMX mbean server, RMI invocations and much more. The +simulator is able to deal with any kind of endpoint component that is supported in Citrus framework.

+
+
+

The generic endpoint support is activated by setting the property citrus.simulator.endpoint.enabled=true. You can do so in the basic application.properties +file or via system property or environment variable setting.

+
+
+
+
import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Simulator {
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+}
+
+
+
+

The citrus.simulator.endpoint.enabled property performs some auto configuration steps and loads required beans for the Spring application context +in the Spring boot application. Once we use that feature we can have any Citrus endpoint component as inbound source for simulator scenarios. This means +we can have a mail server or a RMI server that is simulated with proper response messages.

+
+
+

7.1. Configuration

+
+

As we are using generic Citrus endpoint components as inbound source we need to configure those endpoint components. The most comfortable way is to +add a SimulatorEndpointComponentAdapter implementation to the classpath. The adapter provides several configuration methods.

+
+
+
+
public abstract class SimulatorEndpointComponentAdapter implements SimulatorEndpointComponentConfigurer {
+
+    @Override
+    public abstract Endpoint endpoint(ApplicationContext applicationContext);
+
+    @Override
+    public boolean useSoapEnvelope() {
+        return false;
+    }
+
+    @Override
+    public ScenarioMapper scenarioMapper() {
+        return new ContentBasedXPathScenarioMapper().addXPathExpression("local-name(/*)");
+    }
+}
+
+
+
+

The adapter defines methods that configure the endpoint component used as inbound source. As usual we can set the scenario mapper implementation or +add automatic SOAP envelope support.

+
+
+

More importantly we need to define an inbound endpoint that is used as source for scenarios. Let’s have a simple endpoint component adapter example.

+
+
+
+
import org.citrusframework.simulator.endpoint.SimulatorEndpointComponentAdapter;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Simulator extends SimulatorEndpointComponentAdapter {
+
+    public static void main(String[] args) {
+        SpringApplication.run(Simulator.class, args);
+    }
+
+    @Override
+    public Endpoint endpoint(ApplicationContext applicationContext) {
+        MailServer mailServer = new MailServer();
+        mailServer.setPort(2222);
+        mailServer.setAutoStart(true);
+
+        return mailServer;
+    }
+
+    @Override
+    public ScenarioMapper scenarioMapper() {
+        return new ContentBasedXPathScenarioMapper()
+                        .addNamespaceMapping("mail", "http://www.citrusframework.org/schema/mail/message")
+                        .addXPathExpression("/mail:mail-message/mail:subject");
+    }
+}
+
+
+
+

The custom adapter defines a Citrus mail server endpoint that should be used as inbound source. Any mail message that arrives at this mail server component will +trigger a new simulator scenario then. Also we overwrite the scenario mapper implementation. The sample uses a content based XPath mapper that uses the mail subject +value as scenario mapping key.

+
+
+

As mail server endpoint support is not included by default in the simulator we need to add some Citrus dependencies to our project. In Maven we simply add the following dependency to the project POM.

+
+
+
+
<!-- Citrus web service support -->
+<dependency>
+  <groupId>com.consol.citrus</groupId>
+  <artifactId>citrus-mail</artifactId>
+  <version>${citrus.version}</version>
+</dependency>
+
+
+
+

This configuration would lead us to a mail server that responds to incoming mail messages base on the mail subject. So we can have several simulator +scenarios for different mail messages.

+
+
+
+
@Scenario("Hello")
+public class HelloScenario extends AbstractSimulatorScenario {
+
+    @Override
+    public void run(ScenarioDesigner scenario) {
+        scenario
+            .receive()
+            .payload("<mail-message xmlns=\"http://www.citrusframework.org/schema/mail/message\">" +
+                        "<from>user@citrusframework.org</from>" +
+                        "<to>citrus@citrusframework.org</to>" +
+                        "<cc></cc>" +
+                        "<bcc></bcc>" +
+                        "<subject>Hello</subject>" +
+                        "<body>" +
+                            "<contentType>text/plain; charset=utf-8</contentType>" +
+                            "<content>Say Hello!</content>" +
+                        "</body>" +
+                    "</mail-message>");
+
+        scenario
+            .send()
+            .payload("<mail-response xmlns=\"http://www.citrusframework.org/schema/mail/message\">" +
+                        "<code>250</code>" +
+                        "<message>OK</message>" +
+                    "</mail-response>");
+    }
+}
+
+
+
+

The scenario implementation above is listening for mail messages of subject Hello. The +mail XML marshalling is automatically done by Citrus. This is the usual way how the Citrus mail component handles mail messages and responses. That means we can use the default Citrus +features in our simulator, too. The scenario sends back a positive mail response to the calling client.

+
+
+

This is how we can use any Citrus endpoint component as simulator inbound source. This gives us the opportunity to support a huge set of message transports and +message types in our simulator applications. Each incoming request on the endpoint component triggers a new simulator scenario.

+
+
+
+
+
+

8. User interface

+
+
+

The simulator application is started as a Spring boot web application. If you open your browser and point to the running simulator instance at http://localhost:8080 +you will see the default welcome page.

+
+
+

default ui

+
+
+

The default ui is straight forward and limited to viewing Json response data returned by the simulator REST API. Fortunately there is also a more comfortable user interface based +on Angular2 available. Users can then access an administrative web user interface in order to review the simulator status and list all executed scenarios and their outcome.

+
+
+

The simulator Angular2 user interface comes as dependency that is added to the simulator project. In Maven we simply add the following dependency:

+
+
+
+
<!-- Simulator web ui -->
+<dependency>
+  <groupId>com.consol.citrus</groupId>
+  <artifactId>citrus-simulator-ui</artifactId>
+  <version>${citrus.simulator.version}</version>
+</dependency>
+
+
+
+

Now you can start the simulator and open your browser pointing to the simulator UI at http://localhost:8080. You will see the extended +web application user interface that gives you information about the simulator.

+
+
+

8.1. Dashboard

+
+

The simulator dashboard gives you a quick overview of the numbers and facts on your simulator project.

+
+
+

dashboard

+
+
+
+

8.2. Scenarios

+
+

The user interface lists all available scenarios. These are scenarios that are available for automatic mapping when incoming requests are handled.

+
+
+

scenario list

+
+
+

In case you open one of the listed scenarios you get a detailed information page of that specific scenario.

+
+
+

scenario details

+
+
+
+

8.3. Activities

+
+

Each time a scenario instance is started the user interface will display that scenario as running activity.

+
+
+

scenario activity list

+
+
+

You can view the details of that scenario activity when opening a specific activity entry.

+
+
+

scenario activity details

+
+
+
+

Messages

+
+

When scenarios get executed they usually exchange several messages in request and response communication. These messages are also stored in the simulator so you +can review all exchanged messages on a scenario in the user interface.

+
+
+

messages

+
+
+

Open a specific message entry and you will see the details such as header information and message payload details.

+
+
+
+
+
+

9. Starter

+
+
+

Usually the simulator provides simulation for server APIs so clients are able to call the simulator. In some cases things may go the other way round. The simulator is able to start +scenarios via web user interface. This way the simulator is able to start a communication instead of waiting for requests to arrive. In this case the simulator is able to +act as client in order to call other server APIs.

+
+
+

This mechanism is covered by starter implementations on the simulator. In fact the starter implementations are simulator scenarios that also implement the ScenarioStarter +interface.

+
+
+
+
public interface ScenarioStarter extends SimulatorScenario {
+
+    default Collection<ScenarioParameter> getScenarioParameters() {
+        return Collections.EMPTY_LIST;
+    }
+}
+
+
+
+

The ScenarioStarter extends the SimulatorScenario interface. So each scenario can also act as a starter. See the following sample starter implementation.

+
+
+
+
@Starter("HelloStarter")
+public class HelloStarter extends AbstractScenarioStarter {
+
+    @Autowired
+    private JmsEndpoint outboundEndpoint;
+
+    @Override
+    public void run(ScenarioRunner scenario) {
+        scenario.send(outboundEndpoint)
+                .payload("Hello from simulator!");
+    }
+}
+
+
+
+

The starter class is annotated with @Starter annotation and gets a unique name. The class extends from AbstractScenarioStarter for most comfortable setup. In the run +method we can add custom scenario logic as usual. Inthe sample above we send a JMS message to some endpoint that has been injected as Spring bean component.

+
+
+

All these starter implementations in the project are automatically loaded in the simulator user interface so you can launch those scenarios.

+
+
+

scenario starter list

+
+
+

The list of available scenarios now also contains the starter implementations. The launch button brings you to the starter detail page.

+
+
+

scenario starter details

+
+
+

The scenario starter is able to define parameters that need to be filled before the starter is executed. In case you press the Launch button the scenario is executed with those parameters. +The parameter HTML form is auto generated from the starter class. In the following section we see how the starter class is able to define those parameters.

+
+
+

9.1. Starter Parameter

+
+

Starter implementations are executable in the simulator user interface. The starter is able to define parameters that should be filled out before the starter performs its actions. In +the starter class you can specify the parameters as follows:

+
+
+
+
@Starter("HelloStarter")
+public class HelloStarter extends AbstractScenarioStarter {
+
+    @Autowired
+    private JmsEndpoint outboundEndpoint;
+
+    @Override
+    public void run(ScenarioRunner scenario) {
+        scenario.send(outboundEndpoint)
+                .payload("${greeting}");
+    }
+
+    @Override
+    public List<ScenarioParameter> getScenarioParameters() {
+        List<ScenarioParameter> scenarioParameters = new ArrayList<>();
+
+        // greeting (text box)
+        scenarioParameters.add(new ScenarioParameterBuilder()
+                .name("greeting")
+                .label("Greeting Text")
+                .required()
+                .textbox()
+                .value("Hi there!")
+                .build());
+
+        return scenarioParameters;
+    }
+}
+
+
+
+

The sample above defines a parameter called greeting. The parameter is of type textbox and has a default value Hi there!. This parameter +information is used to aut generate a HTML form in the simulator user interface. You can specify as multiple parameters of different types.

+
+
+
Parameter types
+ + + + + + + + + + + + + +
+textbox + +

Renders a normal HTML input field

+
+textarea + +

Renders a textarea input field

+
+dropdown + +

Renders a dropdown select field with predefined options

+
+
+
+

Parameters can be required or optional and receive a label that is displayed in the HTML form. Also you can add default values.

+
+
+
+
+
+

10. Samples

+
+
+

Documentation can only provide theoretical insights of what the application is capable to do or not to do. Good sample projects +help both beginners and experts to reach complete understanding of how the simulator works in different environments.

+
+
+

Following from that we try to provide sample project that demonstrate the simulator usage in real world examples. Please see the following +sample projects with complete code available on github.

+
+ + + + + +
+
+
+ +
+
+
Reading material
+ + + + + + + + + + + + + + + + + +
+Citrus manual + +

gives you a detailed description of all Citrus features

+
+Sample projects + +

demonstrate typical simulator scenarios with different message transports

+
+ChangeLog + +

shows the release history

+
+Contributing + +

explains how you can contribute to this project

+
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/reference/2.1.1/pdf/citrus-simulator-reference-2.1.1.pdf b/reference/2.1.1/pdf/citrus-simulator-reference-2.1.1.pdf new file mode 100644 index 000000000..cc7ffc4cd Binary files /dev/null and b/reference/2.1.1/pdf/citrus-simulator-reference-2.1.1.pdf differ diff --git a/reference/html/index.html b/reference/html/index.html index 42bf8ec17..02ff7665e 100644 --- a/reference/html/index.html +++ b/reference/html/index.html @@ -454,8 +454,8 @@

Citrus Simulator

Authors: The Citrus Community
-version 2.1.0, -2022-03-04 +version 2.1.1, +2024-01-26
citrus-simulator
@@ -569,7 +569,7 @@

Citrus Simulator