<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: mydeveloperplanet</title>
    <description>The latest articles on DEV Community by mydeveloperplanet (@mydeveloperplanet).</description>
    <link>https://dev.to/mydeveloperplanet</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F565422%2F16cad529-39e8-4a7c-b919-744c99f0e28d.png</url>
      <title>DEV Community: mydeveloperplanet</title>
      <link>https://dev.to/mydeveloperplanet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mydeveloperplanet"/>
    <language>en</language>
    <item>
      <title>How to Deploy a Spring Boot App on AWS ECS Cluster</title>
      <dc:creator>mydeveloperplanet</dc:creator>
      <pubDate>Wed, 15 Sep 2021 15:42:12 +0000</pubDate>
      <link>https://dev.to/mydeveloperplanet/how-to-deploy-a-spring-boot-app-on-aws-ecs-cluster-146a</link>
      <guid>https://dev.to/mydeveloperplanet/how-to-deploy-a-spring-boot-app-on-aws-ecs-cluster-146a</guid>
      <description>&lt;p&gt;In this post, you will learn how to setup an AWS ECS (Elastic Container Service) Cluster. You will create the Cluster and deploy a Docker image containing a Spring Boot App. Enjoy!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;Amazon Elastic Container Service is a managed container orchestration service which allows you to deploy and scale containerized applications. An overview of the features and pricing can be found at the &lt;a href="https://aws.amazon.com/ecs" rel="noopener noreferrer"&gt;AWS website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;ECS consists out of a few components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Elastic Container Repository (ECR)&lt;/strong&gt;: A Docker repository to store your Docker images (similar as DockerHub but now provisioned by AWS).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task Definition&lt;/strong&gt;: A versioned template of a task which you would like to run. Here you will specify the Docker image to be used, memory, CPU, etc. for your container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ECS Cluster&lt;/strong&gt;: The Cluster definition itself where you will specify how many instances you would like to have and how it should scale.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service&lt;/strong&gt;: Based on a Task Definition, you will deploy the container by means of a Service into your Cluster.
This is basically everything you need to know in order to get started with this hands-on tutorial. The sources being used in this post are available at &lt;a href="https://github.com/mydeveloperplanet/MyAWSPlanet" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You will create a Docker image for a basic Spring Boot Application, upload it to ECR, create a Task Definition for the image, create a Cluster and deploy the container by means of a Service to the Cluster. At the end, you will also do something similar but including an Application Load Balancer.&lt;/p&gt;

&lt;p&gt;At the time of writing, a new UI is being developed. For most of the actions described in this post, the &lt;strong&gt;New ECS Experience&lt;/strong&gt; toggle has been enabled.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Create the App
&lt;/h2&gt;

&lt;p&gt;The Spring Boot App is a basic application with a Hello Rest endpoint which returns a hello message including the host where the application is running. The app is used before in a &lt;a href="https://mydeveloperplanet.com/2021/06/23/how-to-create-an-aws-alb-and-asg/" rel="noopener noreferrer"&gt;previous post&lt;/a&gt;. The controller is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hello"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hello AWS!"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;InetAddress&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InetAddress&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLocalHost&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;" From host: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UnknownHostException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to be able to create a Docker image, you need to add the &lt;code&gt;dockerfile-maven-plugin&lt;/code&gt; to the &lt;code&gt;pom&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;java.version&amp;gt;&lt;/span&gt;11&lt;span class="nt"&gt;&amp;lt;/java.version&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;docker.image.prefix&amp;gt;&lt;/span&gt;mydeveloperplanet&lt;span class="nt"&gt;&amp;lt;/docker.image.prefix&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
        ...
        &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.spotify&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;dockerfile-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.4.12&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;build&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;push&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;

            &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;repository&amp;gt;&lt;/span&gt;${docker.image.prefix}/myawsplanet&lt;span class="nt"&gt;&amp;lt;/repository&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;tag&amp;gt;&lt;/span&gt;${project.version}&lt;span class="nt"&gt;&amp;lt;/tag&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;buildArgs&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;JAR_FILE&amp;gt;&lt;/span&gt;target/${project.build.finalName}.jar&lt;span class="nt"&gt;&amp;lt;/JAR_FILE&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/buildArgs&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also need to add a &lt;code&gt;Dockerfile&lt;/code&gt; to the root of the repository. The &lt;code&gt;Dockerfile&lt;/code&gt; is following some best practices from a &lt;a href="https://mydeveloperplanet.com/2019/01/16/secure-docker-in-production/" rel="noopener noreferrer"&gt;previous post&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM openjdk:11-jdk
VOLUME /tmp

RUN useradd -d /home/appuser -m -s /bin/bash appuser
USER appuser

HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost:8080/actuator/health/ || exit 1

ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the build in order to create the jar file and the corresponding Docker image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mvn clean verify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify whether the Docker image is available in your local Docker repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker images
REPOSITORY                                          TAG                 IMAGE ID            CREATED             SIZE
mydeveloperplanet/myawsplanet                       0.0.1-SNAPSHOT      765984f7cfc2        24 seconds ago      666MB

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Upload Image to ECR
&lt;/h2&gt;

&lt;p&gt;Now that you have created the Docker image, you need to upload it to ECR, the AWS Docker repository. Navigate in AWS to the ECS Service and select in the left menu the &lt;strong&gt;Repositories&lt;/strong&gt; section. First thing to do, is to create a repository by clicking the &lt;strong&gt;Create repository&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9urzave03rb6m6p4rjnk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9urzave03rb6m6p4rjnk.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give the repository a recognizable name like &lt;strong&gt;mydeveloperplanet/myawsplanet&lt;/strong&gt; and click the &lt;strong&gt;Create repository&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcg41k162quc66ph56d0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcg41k162quc66ph56d0.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to see how you can push the Docker image to the repository, click the &lt;strong&gt;View push commands&lt;/strong&gt; button which is available in the repository overview.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pgq08klwn3wasru06gb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pgq08klwn3wasru06gb.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Execute step 1, which will provide you temporary credentials in order to be able to gain access to the repository. The &lt;code&gt;&amp;lt;account ID&amp;gt;&lt;/code&gt; will be replaced with your AWS account ID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; eu-west-3 | docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; &amp;lt;account ID&amp;gt;.dkr.ecr.eu-west-3.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Skip step2, building the Docker image is already executed by means of the Maven build. In step 3, adjust the command in order to use version &lt;code&gt;0.0.1-SNAPSHOT&lt;/code&gt; instead of &lt;code&gt;latest&lt;/code&gt; for identifying the local Docker image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker tag mydeveloperplanet/myawsplanet:0.0.1-SNAPSHOT &amp;lt;account ID&amp;gt;.dkr.ecr.eu-west-3.amazonaws.com/mydeveloperplanet/myawsplanet:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In step 4, you push the local Docker image to the remote AWS Docker repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker push &amp;lt;account ID&amp;gt;.dkr.ecr.eu-west-3.amazonaws.com/mydeveloperplanet/myawsplanet:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After successful upload, the Docker image is added to the repository.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2fd5ynwrcgrrnz3stpxn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2fd5ynwrcgrrnz3stpxn.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Create Task Definition
&lt;/h2&gt;

&lt;p&gt;Now that the Docker image is available in ECR, next thing to do is to create a Task Definition by creating the &lt;strong&gt;Create new Task Definition&lt;/strong&gt; button in the &lt;strong&gt;Task Definitions&lt;/strong&gt; section (left menu).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv3r7a5brwngedvpqxg3s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv3r7a5brwngedvpqxg3s.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In step 1, choose for an EC2 self managed task and click the &lt;strong&gt;Next step&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2sd8us1s4lwjff7tqay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2sd8us1s4lwjff7tqay.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In step 2, give the task definiton the name &lt;strong&gt;myawsplanet&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuvs9bfhzavkum2yq9kf1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuvs9bfhzavkum2yq9kf1.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select a &lt;strong&gt;Task execution role&lt;/strong&gt; in the &lt;strong&gt;Task execution IAM role&lt;/strong&gt; section. This role is necessary for pulling the Docker image. If the role does not exist yet, select &lt;strong&gt;Create new role&lt;/strong&gt; in the dropdown list. The &lt;strong&gt;Task memory&lt;/strong&gt; and &lt;strong&gt;Task CPU&lt;/strong&gt; fields are optional, leave them empty for now. In the screenshot below, the container is already added. See below the screenshot how to accomplish this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr458e84w0iz7fgd5brjv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr458e84w0iz7fgd5brjv.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;Add container&lt;/strong&gt; button. Give the container the name &lt;strong&gt;myawsplanet&lt;/strong&gt;. Fill in the Image to be pulled. This should be the Image URI of the Docker image from the ECR repository followed by the tag. Set the &lt;strong&gt;Memory Limits&lt;/strong&gt; to 300 and add a &lt;strong&gt;Port mappings&lt;/strong&gt; entry for host port 8080 to the container port 8080 (the Spring Boot application runs on port 8080 inside the container). Finally, click the &lt;strong&gt;Add&lt;/strong&gt; button in order to add the container to the Task Definition.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65b3rmpaoysiyne0djkl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65b3rmpaoysiyne0djkl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The only thing left to do is to finalize step 2 by clicking the &lt;strong&gt;Create&lt;/strong&gt; button at the bottom of the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Create Cluster
&lt;/h2&gt;

&lt;p&gt;Navigate in the left menu to the &lt;strong&gt;Clusters&lt;/strong&gt; section and click the &lt;strong&gt;Create cluster&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;In step 1, choose &lt;strong&gt;EC2 Linux + Networking&lt;/strong&gt; and click the &lt;strong&gt;Next step&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpck9cp82b3c1lb1ez8a6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpck9cp82b3c1lb1ez8a6.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In step 2, give the Cluster the name &lt;strong&gt;myawsplanet&lt;/strong&gt; and choose the &lt;strong&gt;t2.micro&lt;/strong&gt; as &lt;strong&gt;EC2 instance type&lt;/strong&gt;. This will allow you to remain in the Free Tier. Leave the other options as default. This will launch 1 EC2 instance into your Cluster.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1hg0ob1c1yy092bqft9s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1hg0ob1c1yy092bqft9s.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Networking&lt;/strong&gt; section, choose the default VPC and select all available subnets. Choose &lt;strong&gt;Create a new security group&lt;/strong&gt; as &lt;strong&gt;Security group&lt;/strong&gt;. You can also choose a &lt;strong&gt;Key pair&lt;/strong&gt; if you want to be able to SSH to the EC2 instances. In the screenshot below, SSH access is not enabled.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya3rvjk8dncfecijdlzp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya3rvjk8dncfecijdlzp.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The new security group must allow all traffic for port 8080 otherwise the Rest endpoint will not be accessible. This is configured in the &lt;strong&gt;Security group inbound rules&lt;/strong&gt; section. Next, choose a previously created &lt;strong&gt;Container instance IAM role&lt;/strong&gt; or choose &lt;strong&gt;Create new role&lt;/strong&gt; when this is the first time you are going to use this. Click the &lt;strong&gt;Create&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flssiuaa4xak2qpuj81i7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flssiuaa4xak2qpuj81i7.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a few minutes, the ECS Cluster is running. Navigate to the EC2 service and notice that the Auto Scaling group has been created and one EC2 instance is running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxs4ekzjnlnz2zg53pns6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxs4ekzjnlnz2zg53pns6.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The EC2 instance:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6u7pm4qpcfd2t53ccr3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6u7pm4qpcfd2t53ccr3.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Create Service
&lt;/h2&gt;

&lt;p&gt;You have created a Cluster and a Task Definition. Now it is time to deploy the Task Definition containing the configuration for the Docker container into the Cluster. Navigate to the Cluster and click the &lt;strong&gt;Deploy&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frr632hxhzxow93gtvxvp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frr632hxhzxow93gtvxvp.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Expand the &lt;strong&gt;Compute configuration (advanced)&lt;/strong&gt; section and choose &lt;strong&gt;EC2&lt;/strong&gt; as &lt;strong&gt;Launch type&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs8ejx3yavjqjcqppegbw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs8ejx3yavjqjcqppegbw.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Deployment configuration&lt;/strong&gt; section, choose the Task Definition &lt;strong&gt;myawsplanet&lt;/strong&gt; as &lt;strong&gt;Family&lt;/strong&gt; and choose the latest revision. As you can see, task definitons are version controlled. Give the Service the name &lt;strong&gt;myawsplanetservice&lt;/strong&gt; and create the Service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7tp2ucsaihdh7d8saetk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7tp2ucsaihdh7d8saetk.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the EC2 service and copy the public URL of the EC2 instance. Use this URL in order to verify whether the endpoint is accessible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://ec2-13-36-172-189.eu-west-3.compute.amazonaws.com:8080/hello
Hello AWS! From host: a035a951c3c8/172.17.0.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Create Service With ALB
&lt;/h2&gt;

&lt;p&gt;With the previous configuration it is possible to run more tasks with more EC2 instances. However, it is not possible to route the traffic for port 8080 to different containers running on 1 EC2 instance. In other words, you are limited to run 1 task at each EC2 instance. Solution for this is to create a Service with an Application Load Balancer (ALB). It is, however, not possible to edit an existing Service to use an ALB. Therefore, you need to create a new Service. The Task Definition itself can be reused.&lt;/p&gt;

&lt;p&gt;First, you will remove the existing Service. Navigate to the Service and click the &lt;strong&gt;Edit&lt;/strong&gt; button. Set the &lt;strong&gt;Desired tasks&lt;/strong&gt; to 0. This is necessary before you can remove the Service (do not mind the Revision number in the screenshot below, I just messed around while creating this blog). Click the &lt;strong&gt;Update&lt;/strong&gt; button and remove the Service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyr4wlookqsed38bfnb2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyr4wlookqsed38bfnb2y.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the Task Definition and click the &lt;strong&gt;Create new revision&lt;/strong&gt; button. Navigate to the container definition and set the host port to 0. This way, the ALB will choose a random port for the communication between the ALB and each Docker container. Click the &lt;strong&gt;Update&lt;/strong&gt; button in order to update the container definiton and click the &lt;strong&gt;Create&lt;/strong&gt; button in order to create the new Task Definition revision.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8m2hva6v2ri71w0t8fxl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8m2hva6v2ri71w0t8fxl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a new Service just like before, but in the &lt;strong&gt;Load balancing&lt;/strong&gt; section choose for &lt;strong&gt;Application Load Balancer&lt;/strong&gt;. Give the ALB the name &lt;strong&gt;MyAwsAlb&lt;/strong&gt; and define port 8080 where the ALB should listen to. Give &lt;strong&gt;Target&lt;/strong&gt; group the name &lt;strong&gt;ecs2containers&lt;/strong&gt; and choose &lt;strong&gt;HTTP&lt;/strong&gt; as protocol. Finally, click the &lt;strong&gt;Deploy&lt;/strong&gt; button at the bottom of the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8hiffegzuiojsw5a0gwa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8hiffegzuiojsw5a0gwa.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a few minutes, the Service is created. Verify whether the Rest endpoint is accessible using the public IP of the ALB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://MyAwsAlb-4611708.eu-west-3.elb.amazonaws.com:8080/hello
curl: &lt;span class="o"&gt;(&lt;/span&gt;28&lt;span class="o"&gt;)&lt;/span&gt; Failed to connect to MyAwsAlb-4611708.eu-west-3.elb.amazonaws.com port 8080: Connection timed out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A time-out occurs, meaning that something is wrong with the Security Groups.&lt;/p&gt;

&lt;p&gt;Navigate to the EC2 service to the &lt;strong&gt;Security Groups&lt;/strong&gt; section. Create a new Security Group for the ALB where inbound traffic for port 8080 is allowed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3ptljw1oa54lu47kbdc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3ptljw1oa54lu47kbdc.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Attach this Security Group to the ALB via the &lt;strong&gt;Details&lt;/strong&gt; tab in the &lt;strong&gt;Security Group&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;Navigate to the EC2 service to the EC2 Instance. Click the Security Group in the &lt;strong&gt;Security&lt;/strong&gt; tab. Click the &lt;strong&gt;Edit inbound rules&lt;/strong&gt; button in the &lt;strong&gt;Inbound rules&lt;/strong&gt; tab and add an &lt;strong&gt;All Traffic&lt;/strong&gt; rule with the ALB Security Group as source. Remove the existing 8080 rule.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0x0yoi8tjoq553ycsud4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0x0yoi8tjoq553ycsud4.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Verify again whether the end point is accessible and now it works!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://MyAwsAlb-555143254.eu-west-3.elb.amazonaws.com:8080/hello
Hello AWS! From host: 9a71b51064ee/172.17.0.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal was to be able to deploy the image to multiple containers running on a single EC2 instance. Navigate to the Service and change the number of tasks to 2. Verify again with &lt;code&gt;curl&lt;/code&gt; and you will notice that the IP is changing. Two tasks are running at one EC2 instance. It is possible that a &lt;code&gt;502 Bad Gateway&lt;/code&gt; occurs after updating the tasks, but this should stop after a few seconds.&lt;/p&gt;

&lt;p&gt;Finally, let’s activate 2 EC2 instances by updating the ASG in the EC2 service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbz1bc5jjq1jsw09ye2qo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbz1bc5jjq1jsw09ye2qo.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the ECS Service you can set the number of tasks to 4.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38nqjzv3w5v48cgr7osl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38nqjzv3w5v48cgr7osl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the classic view (disable the &lt;strong&gt;New ECS Experience&lt;/strong&gt; toggle), you can see that 2 EC2 instances are running with each 2 tasks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdt93vufg5886lgvtd3w7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdt93vufg5886lgvtd3w7.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;curl&lt;/code&gt; again shows you that the four tasks are interrogated one after another.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://MyAwsAlb-555143254.eu-west-3.elb.amazonaws.com:8080/hello
Hello AWS! From host: 5e97f1d8c823/172.17.0.3
&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://MyAwsAlb-555143254.eu-west-3.elb.amazonaws.com:8080/hello
Hello AWS! From host: ef015b5e2050/172.17.0.2
&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://MyAwsAlb-555143254.eu-west-3.elb.amazonaws.com:8080/hello
Hello AWS! From host: e70d69bb7d02/172.17.0.2
&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://MyAwsAlb-555143254.eu-west-3.elb.amazonaws.com:8080/hello
Hello AWS! From host: 1b2af349eb95/172.17.0.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8. Cleanup
&lt;/h2&gt;

&lt;p&gt;Execute the following steps in order to remove all resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set the number of tasks to 0 at the ECS Service;&lt;/li&gt;
&lt;li&gt;Remove the Cluster, this is at the moment of writing only available in the classic view;&lt;/li&gt;
&lt;li&gt;Click the Task Defintion, select all revisions and via the &lt;strong&gt;Actions&lt;/strong&gt; menu, click the &lt;strong&gt;Deregister&lt;/strong&gt; item;&lt;/li&gt;
&lt;li&gt;Remove the ECR repository;&lt;/li&gt;
&lt;li&gt;Remove the ALB, the Target Group and the Security Group my-&lt;code&gt;ecs-alb-sg&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;In the CloudFormation service, remove the Stack which was created. You did not create this by yourselves, but the ECS Service is using CloudFormation in the background in order to create the necessary resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  9. Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog, you learned how to create and configure an ECS Cluster and deployed a Docker image to it containing a Spring Boot Application. You also learned how to scale the EC2 instances and how to run multiple tasks on multiple EC2 instances using an ALB.&lt;/p&gt;

</description>
      <category>java</category>
      <category>aws</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Create an AWS ALB and ASG</title>
      <dc:creator>mydeveloperplanet</dc:creator>
      <pubDate>Tue, 29 Jun 2021 19:41:03 +0000</pubDate>
      <link>https://dev.to/mydeveloperplanet/how-to-create-an-aws-alb-and-asg-3fc2</link>
      <guid>https://dev.to/mydeveloperplanet/how-to-create-an-aws-alb-and-asg-3fc2</guid>
      <description>&lt;p&gt;In this post, you will learn how to create an AWS Application Load Balancer (ALB) for your EC2 instances running a Spring Boot application. You will also create an Autoscaling Group (ASG) which will simplify the setup and will automatically scale-in and scale-out.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;A load balancer will make it possible to distribute the workload across multiple EC2 instances. A client application will connect to the load balancer without knowing which EC2 instance will handle the request. Because of this, EC2 instances can come and go without impacting your client requests. It is transparent because of the load balancer. It is easy to scale-out and thus to add more EC2 instances when traffic increases, or to scale-in and reduce the number of EC2 instances in order to reduce costs when traffic gets low.&lt;/p&gt;

&lt;p&gt;In this post, you will learn how to setup an Application Load Balancer (note that there are &lt;a href="https://aws.amazon.com/elasticloadbalancing/features/#Product_comparisons" rel="noopener noreferrer"&gt;other types&lt;/a&gt; as well, dependent on your need) which distributes traffic between several EC2 instances running a basic Spring Boot application. Next, you will learn how to use an Autoscaling Group, which will simplify things quite a bit.&lt;/p&gt;

&lt;p&gt;In case that you do not have any knowledge how to create EC2 instances, read a &lt;a href="https://mydeveloperplanet.com/2021/06/09/how-to-create-an-aws-ec2-vm/" rel="noopener noreferrer"&gt;previous blog&lt;/a&gt;. The sources used in this post are available at &lt;a href="https://github.com/mydeveloperplanet/MyAWSPlanet" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. The resources being used in this post will not cost you a thing, only Free Tier is used.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Sample Application
&lt;/h2&gt;

&lt;p&gt;The basic application contains of a simple Hello endpoint which returns a message containing the host name of the machine which handled the request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hello"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hello AWS!"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;InetAddress&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InetAddress&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLocalHost&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;" From host: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UnknownHostException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also the health endpoint of Spring Actuator is added which you can use in order to enable the health check of the EC2 instance.&lt;/p&gt;

&lt;p&gt;In case you want to run the application locally, just run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mvn spring-boot:run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify whether the endpoints are available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:8080/hello
Hello AWS! From host: &amp;lt;name of your machine&amp;gt;/127.0.1.1
&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:8080/actuator/health
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt;:&lt;span class="s2"&gt;"UP"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the jar file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mvn clean verify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The jar file is uploaded to &lt;a href="https://github.com/mydeveloperplanet/MyAWSPlanet/releases/tag/v0.0.1-alpha" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; as a release in order to be able to download it to the EC2 instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Create the EC2 Instances
&lt;/h2&gt;

&lt;p&gt;Create 2 EC2 instances in two different availability zones with the following User Data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
yum &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;java-11-amazon-corretto-headless
wget https://github.com/mydeveloperplanet/MyAWSPlanet/releases/download/v0.0.1-alpha/MyAWSPlanet-0.0.1-SNAPSHOT.jar
java &lt;span class="nt"&gt;-jar&lt;/span&gt; MyAWSPlanet-0.0.1-SNAPSHOT.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During startup of the EC2 instance, Java 11 is downloaded and installed, the jar file is downloaded and the jar file is started.&lt;/p&gt;

&lt;p&gt;Create a new Security Group and leave the default SSH access inbound rule for now. Name the Security Group &lt;strong&gt;ec2-sg&lt;/strong&gt;. Now launch the EC2 instances.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Create the ALB
&lt;/h2&gt;

&lt;p&gt;In the left menu, navigate to &lt;strong&gt;Load Balancers&lt;/strong&gt; in the &lt;strong&gt;Load Balancing&lt;/strong&gt; section and click the &lt;strong&gt;Create Load Balancer&lt;/strong&gt; button. Here you can choose the type of load balancer you want to use. Choose &lt;strong&gt;Application Load Balancer&lt;/strong&gt; by clicking the &lt;strong&gt;Create&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9o9456vl41q97lg1lzcg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9o9456vl41q97lg1lzcg.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Step 1&lt;/strong&gt;, you give the load balancer the name &lt;strong&gt;MyFirstLoadBalancer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmn1ubk2guq94zeekljxx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmn1ubk2guq94zeekljxx.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the listener to port &lt;strong&gt;8080&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cd34gs2so1uyfu2mpl2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cd34gs2so1uyfu2mpl2.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You also enable the availability zones for the load balancer. Check in which availability zones your EC2 instances are running and enable the same availability zones. Click the &lt;strong&gt;Next: Configure Security Settings&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0k0i185fr5b3cpmzcpk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0k0i185fr5b3cpmzcpk.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Step 2&lt;/strong&gt;, just click the &lt;strong&gt;Next: Configure Security Groups&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Step 3&lt;/strong&gt;, create a new Security Group &lt;strong&gt;alb-sg&lt;/strong&gt; for your ALB allowing HTTP traffic to port 8080. Click the &lt;strong&gt;Next: Configure Routing&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjw7gbhcv77mmrkndjhj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjw7gbhcv77mmrkndjhj.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Step 4&lt;/strong&gt;, you need to create a &lt;strong&gt;Target Group&lt;/strong&gt;. A target group can be a number of EC2 instances the ALB will send traffic to. Name the target group &lt;strong&gt;MyFirstTargetGroup&lt;/strong&gt;, set the port to &lt;strong&gt;8080&lt;/strong&gt; and set the health check path to &lt;strong&gt;/actuator/health&lt;/strong&gt;. Click the &lt;strong&gt;Next: Register Targets&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fac1nmdgkualqico0vzfh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fac1nmdgkualqico0vzfh.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Step 5&lt;/strong&gt;, you need to add the EC2 instances you want to include in the target group. Select both EC2 instances and click the &lt;strong&gt;Add to registered&lt;/strong&gt; button. Click the &lt;strong&gt;Next: Review&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft55qq1ltcf9ftpbtci6z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft55qq1ltcf9ftpbtci6z.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the end, you are able to review all the settings and click the &lt;strong&gt;Create&lt;/strong&gt; button. You need to wait some time before the ALB is active.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvgzb9osk4k6091z92s5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvgzb9osk4k6091z92s5.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try to invoke the URL with the DNS Name of the load balancer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://MyFirstLoadBalancer-1212513449.eu-central-1.elb.amazonaws.com:8080/hello
&amp;lt;html&amp;gt;
&amp;lt;&lt;span class="nb"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;lt;title&amp;gt;504 Gateway Time-out&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;center&amp;gt;&amp;lt;h1&amp;gt;504 Gateway Time-out&amp;lt;/h1&amp;gt;&amp;lt;/center&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The time-out is expected. The EC2 instances only allow SSH traffic and no HTTP traffic. In the left menu, navigate to &lt;strong&gt;Security Groups&lt;/strong&gt; in the &lt;strong&gt;Network &amp;amp; Security&lt;/strong&gt; section. Select the &lt;strong&gt;ec2-sg&lt;/strong&gt; security group and click the &lt;strong&gt;Edit inbound rules&lt;/strong&gt; button of the &lt;strong&gt;Inbound rules&lt;/strong&gt; tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzq4z9xzms5biig1q653i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzq4z9xzms5biig1q653i.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add a rule which allows HTTP traffic over port 8080. As a source, you choose the security group &lt;strong&gt;alb-sg&lt;/strong&gt; of your ALB. This means that your EC2 instances cannot be reached directly over HTTP but only via the load balancer. Click the &lt;strong&gt;Save rules&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1x0qdrexvflrga19o5qb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1x0qdrexvflrga19o5qb.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try to invoke the URL again and now the Hello message is returned. It will be more or less equally divided between the two machines when you invoke it several times.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://MyFirstLoadBalancer-1212513449.eu-central-1.elb.amazonaws.com:8080/hello
Hello AWS! From host: ip-172-31-2-55.eu-central-1.compute.internal/172.31.2.55
&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://MyFirstLoadBalancer-1212513449.eu-central-1.elb.amazonaws.com:8080/hello
Hello AWS! From host: ip-172-31-2-55.eu-central-1.compute.internal/172.31.2.55
&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://MyFirstLoadBalancer-1212513449.eu-central-1.elb.amazonaws.com:8080/hello
Hello AWS! From host: ip-172-31-2-55.eu-central-1.compute.internal/172.31.2.55
&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://MyFirstLoadBalancer-1212513449.eu-central-1.elb.amazonaws.com:8080/hello
Hello AWS! From host: ip-172-31-2-55.eu-central-1.compute.internal/172.31.2.55
&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://MyFirstLoadBalancer-1212513449.eu-central-1.elb.amazonaws.com:8080/hello
Hello AWS! From host: ip-172-31-21-171.eu-central-1.compute.internal/172.31.21.171
&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://MyFirstLoadBalancer-1212513449.eu-central-1.elb.amazonaws.com:8080/hello
Hello AWS! From host: ip-172-31-21-171.eu-central-1.compute.internal/172.31.21.171
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the &lt;strong&gt;Target Groups&lt;/strong&gt; in the &lt;strong&gt;Load Balancing&lt;/strong&gt; section and take a look at the health of the instances. Here you can see that both EC2 instances are healthy based on the configured health check.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xz8bfqnrtobnzkfabig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xz8bfqnrtobnzkfabig.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to cleanup everything, you need to delete the load balancer, the target group, terminate the EC2 instances, delete the EC2 security group and finally delete the ALB security group. If you want to continue to follow this blog, only terminate the EC2 instances, you will need the other items in the next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Create the ASG
&lt;/h2&gt;

&lt;p&gt;Creating an ALB is easy and has its advantages, but you still need to manually add and remove instances. This problem is solved with an Auto Scaling Group (ASG). Based on a template, scale-out and scale-in will be done automatically based on triggers you define.&lt;/p&gt;

&lt;p&gt;In the left menu, navigate to &lt;strong&gt;Auto Scaling Groups&lt;/strong&gt; in the &lt;strong&gt;Auto Scaling&lt;/strong&gt; section and click the &lt;strong&gt;Create Auto Scaling Group&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;Give the ASG the name &lt;strong&gt;MyFirstASG&lt;/strong&gt;. Next thing to do, is to create a &lt;strong&gt;Launch Template&lt;/strong&gt;. The Launch Template will provide information about how to create EC2 instances. Click the &lt;strong&gt;Create a launch template&lt;/strong&gt; link.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff40s61pxho2z2ff06ww1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff40s61pxho2z2ff06ww1.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A new browser tab is opened where you first give the template the name &lt;strong&gt;MyFirstLaunchTemplate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxu8p9xzqnfgrdje2wnk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxu8p9xzqnfgrdje2wnk.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill in the &lt;strong&gt;AMI&lt;/strong&gt;, the &lt;strong&gt;instance type&lt;/strong&gt; and the &lt;strong&gt;key pair&lt;/strong&gt; to be used just like you did while creating the EC2 instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fogdhieftd9jzxq4gi3ct.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fogdhieftd9jzxq4gi3ct.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Network settings&lt;/strong&gt;, choose the &lt;strong&gt;ec2-sg&lt;/strong&gt; security group.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4m4akb4wnf88mhwjzpc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4m4akb4wnf88mhwjzpc.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Leave the &lt;strong&gt;Network settings&lt;/strong&gt;, &lt;strong&gt;Storage&lt;/strong&gt;, &lt;strong&gt;Resource tags&lt;/strong&gt; and &lt;strong&gt;Network interfaces&lt;/strong&gt; as default. Unfold the &lt;strong&gt;Advanced details&lt;/strong&gt; section and scroll all the way down. Copy the &lt;strong&gt;User Data&lt;/strong&gt; which you used for creating the EC2 instances before into the User data field. Finally, click the &lt;strong&gt;Create launch template&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3pruvhfmjyzew5qpe06b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3pruvhfmjyzew5qpe06b.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Return to the create ASG wizard, click the refresh button next to the &lt;strong&gt;Launch template&lt;/strong&gt; field and select the just created launch template. Click the &lt;strong&gt;Next&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8o72ge5qeg5ublegqh6j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8o72ge5qeg5ublegqh6j.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Step 2&lt;/strong&gt;, select the three subnets and click the &lt;strong&gt;Next&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F701zpthk2evc6apra9jl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F701zpthk2evc6apra9jl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Step 3&lt;/strong&gt;, choose to attach an existing load balancer target groups and select &lt;strong&gt;MyFirstTargetGroup&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fch95lp4f5ihscbd0i92y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fch95lp4f5ihscbd0i92y.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Health Checks&lt;/strong&gt; section, you enable &lt;strong&gt;ELB&lt;/strong&gt;. This way, the ELB will automatically restart when it is unhealthy. Click the &lt;strong&gt;Next&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpyhwvwypcm35g6z1g2fu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpyhwvwypcm35g6z1g2fu.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Step 4&lt;/strong&gt;, you define the &lt;strong&gt;Group size&lt;/strong&gt; with a &lt;strong&gt;Desired capacity&lt;/strong&gt; of 2, a &lt;strong&gt;Minimum capacity&lt;/strong&gt; of 2 and a &lt;strong&gt;Maximum capacity&lt;/strong&gt; of 3.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jpcra6gdtvi16ji9i1o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jpcra6gdtvi16ji9i1o.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the &lt;strong&gt;Scaling policies&lt;/strong&gt;, you can define how the autoscaling should take place when you choose &lt;strong&gt;Target tracking scaling policy&lt;/strong&gt;. For now, just choose &lt;strong&gt;None&lt;/strong&gt;, but it is good to take a look at the different options to automatically scale. Click the &lt;strong&gt;Next&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhlnr1pa1jlg4ksy4tmti.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhlnr1pa1jlg4ksy4tmti.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Step 5&lt;/strong&gt; and &lt;strong&gt;Step 6&lt;/strong&gt;, just click the &lt;strong&gt;Next&lt;/strong&gt; button. It is possible to add notifications and tags, but you will not use it in this tutorial. In &lt;strong&gt;Step 7&lt;/strong&gt;, you can review everything and at the bottom of the page you click the &lt;strong&gt;Create auto scaling group&lt;/strong&gt; button. It may take a few minutes before everything is up-and-running. You will see the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the &lt;strong&gt;Auto Scaling&lt;/strong&gt; section:

&lt;ul&gt;
&lt;li&gt;The Auto Scaling Group&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;In the &lt;strong&gt;Instances&lt;/strong&gt; section:

&lt;ul&gt;
&lt;li&gt;Two EC2 instances which have been created because the minimum and desired capacity was set to 2&lt;/li&gt;
&lt;li&gt;The created Launch Template&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Verify again by means of &lt;code&gt;curl&lt;/code&gt; whether the Hello URL can be accessed.&lt;/p&gt;

&lt;p&gt;Navigate to the Auto Scaling Group and the &lt;strong&gt;Details&lt;/strong&gt; tab and click the &lt;strong&gt;Edit&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0mi40ba2l1x6u0ol0uw5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0mi40ba2l1x6u0ol0uw5.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Increase the desired and minimum capacity and click the &lt;strong&gt;Update&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fonvcepdhj4g0qoyva0km.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fonvcepdhj4g0qoyva0km.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Automatically, a new instance is provisioned. Wait a few minutes until the instance is marked as being healthy in the Target Group and after this, the traffic is distributed over three EC2 instances.&lt;/p&gt;

&lt;p&gt;In order to cleanup everything, you need to delete the Auto Scaling Group (this can take a while), the load balancer, the target group, the EC2 security group and finally delete the ALB security group.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post, you learned how to create an ALB as a single point of access for your EC2 instances. Problem with this setup was that you needed to manually add or remove instances when traffic got high or low. The ASG will solve this problem. It will launch or terminate EC2 instances based on several scaling policies (which you did not configure in this post, but just took a look at).&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>cloud</category>
      <category>java</category>
      <category>aws</category>
    </item>
    <item>
      <title>How to Create an AWS EC2 VM</title>
      <dc:creator>mydeveloperplanet</dc:creator>
      <pubDate>Wed, 16 Jun 2021 16:48:44 +0000</pubDate>
      <link>https://dev.to/mydeveloperplanet/how-to-create-an-aws-ec2-vm-aoc</link>
      <guid>https://dev.to/mydeveloperplanet/how-to-create-an-aws-ec2-vm-aoc</guid>
      <description>&lt;p&gt;In this post, you will learn how easy it is to create a virtual machine with AWS EC2. You will learn the basics and as an example, you will deploy and run a basic Spring Boot application. Enjoy!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/ec2/" rel="noopener noreferrer"&gt;Amazon&lt;/a&gt; EC2 (Elastic Compute Cloud) is one of the most popular AWS services. It allows you to create cloud computing instances in no time. You have the choice out of predefined templates (AMI) for several Operating Systems. &lt;a href="https://aws.amazon.com/ec2/pricing/" rel="noopener noreferrer"&gt;Pricing&lt;/a&gt; is dependent on what kind of purpose the instance needs to serve. Whether you want to have an instance continuously running, or only at a scheduled interval, or you need bare metal, or a dedicated instance, etc. Many options are available for different purposes. Besides that, a lot of different &lt;a href="https://aws.amazon.com/ec2/instance-types/" rel="noopener noreferrer"&gt;configurations&lt;/a&gt; are available to choose from. You will need to choose the right one for your application. In the Free Tier, you only use the micro instance. All of this information is available at the Amazon website and therefore only links to these pages are provided. The offerings may change and you should refer to the Amazon website for the details.&lt;/p&gt;

&lt;p&gt;As a prerequisite for following this blog yourself, you will need to create an AWS account. You can create a Free Tier AWS account, which will allow you to experiment with most of the AWS services for free. See a &lt;a href="https://mydeveloperplanet.com/2020/10/21/how-to-deploy-a-spring-boot-app-to-aws-elastic-beanstalk/" rel="noopener noreferrer"&gt;previous blog&lt;/a&gt; how to setup the free account.&lt;/p&gt;

&lt;p&gt;The sources used in this blog are available at &lt;a href="https://github.com/mydeveloperplanet/MyAWSPlanet" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Your First EC2 Instance
&lt;/h2&gt;

&lt;p&gt;Login to the &lt;strong&gt;AWS Management Console&lt;/strong&gt; and search for the &lt;strong&gt;EC2&lt;/strong&gt; service. This will bring you to the &lt;strong&gt;EC2 Dashboard&lt;/strong&gt;. In the screenshots, the &lt;strong&gt;New EC2 Experience&lt;/strong&gt; is enabled, so it might look a bit different on your side when you did not enable this new layout.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0q52n99gn9890ztmhdzc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0q52n99gn9890ztmhdzc.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the left menu, click on &lt;strong&gt;Instances&lt;/strong&gt; and in order to start creating your first EC2 instance, click the &lt;strong&gt;Launch Instances&lt;/strong&gt; button at the top right corner.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftr7ys7du695uyt7um1nn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftr7ys7du695uyt7um1nn.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A wizard is started which will guide you through the creation process. In step 1, you need to select an AMI. You choose the &lt;strong&gt;Amazon Linux 2 AMI&lt;/strong&gt; which is available in the Free Tier by clicking the &lt;strong&gt;Select&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82vwmuof9ql7j7ft6a15.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82vwmuof9ql7j7ft6a15.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In step 2, you need to choose the &lt;strong&gt;Instance Type&lt;/strong&gt; dependent on your needs concerning CPU, memory, storage and networking capacity. Being in the Free Tier, the choice is easy because there is only one Instance Type available. Choose the &lt;strong&gt;t2.micro&lt;/strong&gt; and click the &lt;strong&gt;Next: Configure Instance Details&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgbmmoekdnxywjrh2xscg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgbmmoekdnxywjrh2xscg.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In step 3, you have the possibility to configure more items for your instance, e.g. the number of instances you want to create. Leave the defaults and click the &lt;strong&gt;Next: Add Storage&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5h78lvmfkr5pbeo95btw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5h78lvmfkr5pbeo95btw.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In step 4, you can configure the storage for your EC2 instance. Leave the defaults and click the &lt;strong&gt;Next: Add Tags&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1oz4wkfyqfa7d2vdfoav.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1oz4wkfyqfa7d2vdfoav.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In step 5, you can add tags. You can add for example a &lt;strong&gt;Name&lt;/strong&gt; tag by clicking the &lt;strong&gt;click to add a Name tag&lt;/strong&gt; link.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsjd8m18n9pukervwyl4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsjd8m18n9pukervwyl4.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Name it &lt;strong&gt;MyFirstEC2Instance&lt;/strong&gt; and click the &lt;strong&gt;Next: Configure Security Group&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcs2i6i8hsm98bu66ydjg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcs2i6i8hsm98bu66ydjg.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In step 6, you need to select or configure a Security Group. A Security Group is like a firewall in front of your EC2 instance. You have several options here, it is advised to only allow the traffic which is absolutely necessary. In this case, you will create a new Security Group &lt;strong&gt;MyFirstSG&lt;/strong&gt; for SSH access from your machine. Go to &lt;a href="https://www.whatismyip.com/" rel="noopener noreferrer"&gt;WhatIsMyIP.com&lt;/a&gt; and fill the IPv4 address followed with &lt;strong&gt;/32&lt;/strong&gt; as a source instead of &lt;strong&gt;0.0.0.0/0&lt;/strong&gt;. Click the &lt;strong&gt;Review and Launch&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9h00ww72jyuni73zsb7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9h00ww72jyuni73zsb7l.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, in step 7, you can review everything and when this is ok, you click the &lt;strong&gt;Launch&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxt2a9limv3lcyqjhqoqu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxt2a9limv3lcyqjhqoqu.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before being launched, a popup window is show where we need to select an existing key pair or create a new one. This is necessary for accessing the instance by means of a SSH key. Create a new key pair &lt;strong&gt;EC2Blog&lt;/strong&gt; and download the key pair. A &lt;strong&gt;EC2Blog.pem&lt;/strong&gt; file is downloaded to your machine. Finally, click the &lt;strong&gt;Launch Instances&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61mmzm2w0taq125n4n3i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61mmzm2w0taq125n4n3i.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Launch Status&lt;/strong&gt; page is displayed, click the &lt;strong&gt;View Instances&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F923karamls3t62qholyp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F923karamls3t62qholyp.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If everything went OK, you first EC2 instance is running now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrvf5bamg4m2kf0if0u3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrvf5bamg4m2kf0if0u3.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. SSH to Instance
&lt;/h2&gt;

&lt;p&gt;Now that the EC2 instance is running, you can actually do something with it. Let’s see if you can SSH to the EC2 instance from your machine (note: the instructions are written for Linux). Open a terminal window from the directory where you saved the key pair &lt;code&gt;.pem&lt;/code&gt; file. In the EC2 AWS console, you need the &lt;strong&gt;Public IPv4 address&lt;/strong&gt; of your instance which is located at the right top corner of the &lt;strong&gt;Details&lt;/strong&gt; page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6dwyi415sm5q5v9f12mb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6dwyi415sm5q5v9f12mb.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the terminal, you enter the following command with your public IP of course.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; EC2Blog.pem ec2-user@3.66.155.101
The authenticity of host &lt;span class="s1"&gt;'3.66.155.101 (3.66.155.101)'&lt;/span&gt; can&lt;span class="s1"&gt;'t be established.
ECDSA key fingerprint is SHA256:/5EorRulTwyFKUJLfTvNPmUlHS9Mt1eTffPD4+9tcwU.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '&lt;/span&gt;3.66.155.101&lt;span class="s1"&gt;' (ECDSA) to the list of known hosts.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0664 for '&lt;/span&gt;EC2Blog.pem&lt;span class="s1"&gt;' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "EC2Blog.pem": bad permissions
ec2-user@3.66.155.101: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, it seems that something is wrong with the permissions of the &lt;code&gt;.pem&lt;/code&gt; file. Let’s check the file permissions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ll EC2Blog.pem 
&lt;span class="nt"&gt;-rw-rw-r--&lt;/span&gt; EC2Blog.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The permissions are indeed a bit too open, so let’s first fix this issue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;400 EC2Blog.pem
&lt;span class="nv"&gt;$ &lt;/span&gt;ll EC2Blog.pem 
&lt;span class="nt"&gt;-r--------&lt;/span&gt; EC2Blog.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retry the SSH command and now it is successful and you can enter commands at your EC2 instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; EC2Blog.pem ec2-user@3.66.155.101

       __|  __|_  &lt;span class="o"&gt;)&lt;/span&gt;
       _|  &lt;span class="o"&gt;(&lt;/span&gt;     /   Amazon Linux 2 AMI
      ___|&lt;span class="se"&gt;\_&lt;/span&gt;__|___|

https://aws.amazon.com/amazon-linux-2/
&lt;span class="o"&gt;[&lt;/span&gt;ec2-user@ip-172-31-44-246 ~]&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another way to connect via SSH is via the EC2 console. Select the instance and click the &lt;strong&gt;Connect&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9wc53ya9slbjpu2zr03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9wc53ya9slbjpu2zr03.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Leave the defaults and click the &lt;strong&gt;Connect&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6q4bzddouy8qjrcy67rx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6q4bzddouy8qjrcy67rx.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will, however, not be able to connect because you restricted SSH access to the IP address of your machine only. Therefore, you need to allow the IP address where Instance Connect will connect from. Go to the &lt;a href="https://ip-ranges.amazonaws.com/ip-ranges.json" rel="noopener noreferrer"&gt;page&lt;/a&gt; where the Amazon IP ranges are defined and search for &lt;strong&gt;EC2_INSTANCE_CONNECT&lt;/strong&gt; for the region you are in. The IP below is for the &lt;strong&gt;eu-central-1&lt;/strong&gt; (Frankfurt) region.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip_prefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.120.181.40/29"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eu-central-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EC2_INSTANCE_CONNECT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network_border_group"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eu-central-1"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the &lt;strong&gt;Security Group&lt;/strong&gt; section (left menu), select the Security Group, the &lt;strong&gt;Inbound rules&lt;/strong&gt; tab and click the &lt;strong&gt;Edit inbound rules&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82fu1saaac46vduqlrrz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82fu1saaac46vduqlrrz.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add a SSH inbound rule for the IP address and click the &lt;strong&gt;Save rules&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbyg2n0wh85t7wk9wop1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbyg2n0wh85t7wk9wop1.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Retry connecting via Connect Instance and you will have access to your EC2 instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ksqqsz4ub108exhaqon.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ksqqsz4ub108exhaqon.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Install Java
&lt;/h2&gt;

&lt;p&gt;The goal is to install a Java Spring Boot application to the EC2 instance. First thing to do, is to install Java. The &lt;a href="https://docs.aws.amazon.com/corretto/latest/corretto-11-ug/amazon-linux-install.html" rel="noopener noreferrer"&gt;installation instructions&lt;/a&gt; can be found at the Amazon website. Execute the following command in the SSH terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;java-11-amazon-corretto-headless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify whether Java is available.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;java &lt;span class="nt"&gt;--version&lt;/span&gt;
openjdk 11.0.11 2021-04-20 LTS
OpenJDK Runtime Environment Corretto-11.0.11.9.1 &lt;span class="o"&gt;(&lt;/span&gt;build 11.0.11+9-LTS&lt;span class="o"&gt;)&lt;/span&gt;
OpenJDK 64-Bit Server VM Corretto-11.0.11.9.1 &lt;span class="o"&gt;(&lt;/span&gt;build 11.0.11+9-LTS, mixed mode&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Problem now is that when you want to launch similar instances (right-click the instance &lt;strong&gt;Image and template – Launch more like this&lt;/strong&gt;), you will need to install Java for each new instance. This can be solved by adding user data during the instance creation.&lt;/p&gt;

&lt;p&gt;Exit the terminal and terminate the EC2 (select the instance, right-click and choose &lt;strong&gt;Terminate instance&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;Create the instance again, just like you did before. In step 3, however, you scroll down to the bottom of the page and add &lt;strong&gt;User data&lt;/strong&gt; with installation of java in the &lt;strong&gt;Advanced Details&lt;/strong&gt; section. The &lt;strong&gt;User data&lt;/strong&gt; will be executed as root, therefore there is no need to add &lt;code&gt;sudo&lt;/code&gt; before the command. Also, add the argument &lt;code&gt;-y&lt;/code&gt; to the &lt;code&gt;yum&lt;/code&gt; command, otherwise the installation will fail because the user will be prompted to continue the installation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhcoghicbekxlrxlsgkc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhcoghicbekxlrxlsgkc.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In step 6, you can select the existing Security Group &lt;strong&gt;MyFirstSG&lt;/strong&gt;. There is also no need to create a new key pair, just choose the existing one &lt;strong&gt;EC2Blog&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When the instance has been started, connect via SSH to the instance. Beware that the public IP has changed!&lt;/p&gt;

&lt;p&gt;Verify whether Java is installed. When something went wrong, checkout the file &lt;code&gt;cloud-init-output.log&lt;/code&gt;. This will give you more information about any errors occurred during startup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo cat&lt;/span&gt; /var/log/cloud-init-output.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Create and Install Spring Boot App
&lt;/h2&gt;

&lt;p&gt;As &lt;a href="https://github.com/mydeveloperplanet/MyAWSPlanet" rel="noopener noreferrer"&gt;Spring Boot Application&lt;/a&gt;, you can use one with a simple Hello World endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hello"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hello AWS!"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;InetAddress&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InetAddress&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLocalHost&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;" From host: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UnknownHostException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the following command in order to generate the jar file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mvn clean verify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the application locally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;java &lt;span class="nt"&gt;-jar&lt;/span&gt; MyAWSPlanet-0.0.1-SNAPSHOT.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify whether you can access the URL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:8080/hello
Hello AWS! From host: &amp;lt;computer name&amp;gt;/127.0.1.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the jar file to the remote EC2 instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;scp &lt;span class="nt"&gt;-i&lt;/span&gt; EC2Blog.pem MyAWSPlanet-0.0.1-SNAPSHOT.jar ec2-user@18.194.164.14:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Via the SSH connection, start the Spring Boot application.&lt;/p&gt;

&lt;p&gt;The URL will not be accessible yet, therefore you need to add an extra inbound rule to the Security Group. Since the application is running at port 8080, you create a custom TCP rule for port 8080 which can be accessed from anywhere.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgzo8idivl6rxsgx0zdac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgzo8idivl6rxsgx0zdac.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Verify whether you can access the URL. You need to use the public IP address of your EC2 instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://18.194.164.14:8080/hello
Hello AWS! From host: ip-172-31-36-30.eu-central-1.compute.internal/172.31.36.30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success! Do not forget to terminate the EC2 instance after you are done experimenting with it and to delete the Security Group if you do not need it anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post, you learned how to create and configure an EC2 instance at Amazon. This is amazingly easy to do. You also learned how to access the remote instance from your laptop and how to install Java on it. Finally, you successfully started a Spring Boot application and accessed it from your machine. All of this has been executed in the Free Tier, so it did not cost you anything!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>cloud</category>
      <category>java</category>
      <category>aws</category>
    </item>
    <item>
      <title>Automated Visual Testing With Robot Framework</title>
      <dc:creator>mydeveloperplanet</dc:creator>
      <pubDate>Tue, 01 Jun 2021 17:48:16 +0000</pubDate>
      <link>https://dev.to/mydeveloperplanet/automated-visual-testing-with-robot-framework-2n8o</link>
      <guid>https://dev.to/mydeveloperplanet/automated-visual-testing-with-robot-framework-2n8o</guid>
      <description>&lt;p&gt;A common problem when automating tests are tests which require a visual comparison to a previous state. This can be a very time consuming task when you need to execute many of these testing tasks. But not anymore, the Robot Framework DocTest library comes to the rescue!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;Even if you do not know what Robot Framework is, it is really worth the effort to continue reading. The DocTest library might save you a lot of testing time, but it requires some knowledge of Robot Framework. In case you do not know what Robot Framework is, read some of the previous posts in order to get a good introduction.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mydeveloperplanet.com/2020/05/19/automated-acceptance-testing-with-robot-framework/"&gt;Automated Acceptance Testing With Robot Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mydeveloperplanet.com/2020/06/02/how-to-write-data-driven-tests-with-robot-framework/"&gt;How to Write Data Driven Tests With Robot Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mydeveloperplanet.com/2020/06/16/create-custom-robot-framework-libraries/"&gt;Create Custom Robot Framework Libraries&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When trying to test your application in an automated way, some tests just cannot be automated. Mainly because these tests require a visual check. Let’s assume that your application makes use of maps which require a regular update, or reports need to be generated and must be verified, etc. These visual tests are probably also very time consuming and there is no guarantee that it is verified correctly. The visual check is done by humans and humans make mistakes. Wouldn’t it be great when you could automate the visual check or at least get notified when something changed compared to a previous version and the output of the test would show you the differences? All of this is now possible with the &lt;a href="https://github.com/manykarim/robotframework-doctestlibrary"&gt;Robot Framework DocTest Library&lt;/a&gt;. It was presented at Robocon 2021. The talk is available &lt;a href="https://robocon.io/#print-is-(not)-dead-%E2%80%93-visual-document-testing-using-robot-framework"&gt;online&lt;/a&gt; and it will take you only half an hour to watch it.&lt;/p&gt;

&lt;p&gt;Enough for the introduction, let’s see how it works. The sources used in this post are available at &lt;a href="https://github.com/mydeveloperplanet/MyDocTestPlanet"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Installation
&lt;/h2&gt;

&lt;p&gt;The installation instructions below are executed with Ubuntu 20.04.2 LTS version. Installation is quite easy using pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; robotframework-doctestlibrary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DocTest makes use of other applications, it is necessary to install these otherwise things can go wrong.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;imagemagick
&lt;span class="nv"&gt;$ &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;tesseract-ocr
&lt;span class="nv"&gt;$ &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;ghostscript
&lt;span class="nv"&gt;$ &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;libdmtx0b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do not install the above applications, using the DocTest keyword &lt;code&gt;Compare Images&lt;/code&gt; will raise the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt; ERROR &lt;span class="o"&gt;]&lt;/span&gt; Error &lt;span class="k"&gt;in &lt;/span&gt;file &lt;span class="s1"&gt;'/&amp;lt;path&amp;gt;/MyDocTestPlanet/visual_test.robot'&lt;/span&gt; on line 2: Importing library &lt;span class="s1"&gt;'DocTest.VisualTest'&lt;/span&gt; failed: ImportError: Unable to find dmtx shared library
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to compare PDF files, ImageMagick might raise the following error from the library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;File could not be converted by ImageMagick to OpenCV Image: testdata/sample_1_page.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trying to convert a PDF to a JPG file using ImageMagick by means of CLI will give you a more clear error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;convert sample.pdf sample.jpg 
convert-im6.q16: attempt to perform an operation not allowed by the security policy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ImageMagick makes use of a &lt;code&gt;policy.xml&lt;/code&gt; file which contains by default the following lines which is the cause of the issue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- disable ghostscript format types --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;policy&lt;/span&gt; &lt;span class="na"&gt;domain=&lt;/span&gt;&lt;span class="s"&gt;"coder"&lt;/span&gt; &lt;span class="na"&gt;rights=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;pattern=&lt;/span&gt;&lt;span class="s"&gt;"PS"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;policy&lt;/span&gt; &lt;span class="na"&gt;domain=&lt;/span&gt;&lt;span class="s"&gt;"coder"&lt;/span&gt; &lt;span class="na"&gt;rights=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;pattern=&lt;/span&gt;&lt;span class="s"&gt;"PS2"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;policy&lt;/span&gt; &lt;span class="na"&gt;domain=&lt;/span&gt;&lt;span class="s"&gt;"coder"&lt;/span&gt; &lt;span class="na"&gt;rights=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;pattern=&lt;/span&gt;&lt;span class="s"&gt;"PS3"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;policy&lt;/span&gt; &lt;span class="na"&gt;domain=&lt;/span&gt;&lt;span class="s"&gt;"coder"&lt;/span&gt; &lt;span class="na"&gt;rights=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;pattern=&lt;/span&gt;&lt;span class="s"&gt;"EPS"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;policy&lt;/span&gt; &lt;span class="na"&gt;domain=&lt;/span&gt;&lt;span class="s"&gt;"coder"&lt;/span&gt; &lt;span class="na"&gt;rights=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;pattern=&lt;/span&gt;&lt;span class="s"&gt;"PDF"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;policy&lt;/span&gt; &lt;span class="na"&gt;domain=&lt;/span&gt;&lt;span class="s"&gt;"coder"&lt;/span&gt; &lt;span class="na"&gt;rights=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;pattern=&lt;/span&gt;&lt;span class="s"&gt;"XPS"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the root of the DocTest repository, a &lt;code&gt;policy.xml&lt;/code&gt; file is present. Replace the file in &lt;code&gt;/etc/ImageMagick-6/policy.xml&lt;/code&gt; file with the one from the repository. The conversion should work now.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. In Practice
&lt;/h2&gt;

&lt;p&gt;In this section, you will learn how to use the library. Note that the GitHub repository of the library also provides some good examples, which will show you how to use the library. Clone the repository and run the test from the root of the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;robot atest/Compare.robot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nevertheless, it is also good to verify whether this will work with items you created yourself. As a base image, some kind of map is created containing a timestamp. All of the images are located in the &lt;code&gt;images&lt;/code&gt; directory. The original drawings &lt;code&gt;*.odg&lt;/code&gt; files are present and the saved &lt;code&gt;*.png&lt;/code&gt; files which will be used in the examples. The base image is &lt;code&gt;base-image.png&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--klQu7Rnc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ua58bqh707g2z1k2i7rg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--klQu7Rnc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ua58bqh707g2z1k2i7rg.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Comparison With Small Difference
&lt;/h3&gt;

&lt;p&gt;The first comparison you will make, is one with a small difference in one of the triangles (&lt;code&gt;base-image-with-triangle-difference.png&lt;/code&gt;). When you would like to compare this visually, you would have a hard time finding the difference because it is such a small one. So, how can we do so by means of the DocTest library? The corresponding test is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| *** Settings *** |
| Library | DocTest.VisualTest

| *** Test Cases *** |
| 01 | [Documentation] | Compare Two Images With Triangle Difference
|    | Compare Images  | images/base-image.png | images/base-image-with-triangle-difference.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You import the library and the only thing you need to do, is to run the &lt;code&gt;Compare Images&lt;/code&gt; keyword with the reference and canditate image. Run the test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;robot visual_test.robot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test obviously fails because there is a difference. You will notice that a directory &lt;code&gt;screenshots&lt;/code&gt; has been created where the diff images are located. But, it is easier to open the &lt;code&gt;log.html&lt;/code&gt; file with Robot Framework results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---xq_alco--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8xy733byejh2ryyw5sgx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---xq_alco--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8xy733byejh2ryyw5sgx.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This result is very useful, you immediately notice where the differences are. As expected, the triangle is indicated as a difference, but also the timestamp. Next to this screenshot, a combined difference image is provided. The results are available in the GitHub repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Comparison With Small Difference Ignoring Timestamp
&lt;/h3&gt;

&lt;p&gt;There is however a problem with the previous comparison. When there are no differences in the map and the screenshots are taken at different points in time, the comparison will still mark the two maps as different. This is probably not what you want. The library provides a solution for this by means of masks. You can define masks in a JSON file where you define which items should be ignored during the comparison. In this case, you create a &lt;code&gt;masks/mask-date.json&lt;/code&gt; file as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"all"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Date Pattern"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pattern"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".*[0-9]{2}-[0-9]{2}-[0-9]{4}"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pattern being used is a simple regular expression. The test case only needs one extra &lt;code&gt;placeholder_file&lt;/code&gt; argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| 02 | [Documentation] | Compare Two Images With Triangle difference and Time Mask
|    | Compare Images  | images/base-image.png | images/base-image-with-triangle-difference.png | placeholder_file=masks/masks-time.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run the test now, it still fails because the triangle has a difference, but in the comparison screenshot, the date is marked with blue, meaning that it has been ignored during the comparison.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FBiSfRgz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n20cnttr1nimpxz1govl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FBiSfRgz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n20cnttr1nimpxz1govl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s verify whether the comparison is successful when the images are identical besides the time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| 03 | [Documentation] | Compare Two Images With Time Mask
|    | Compare Images  | images/base-image.png | images/base-image-with-time-difference.png | placeholder_file=masks/masks-time.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And yes, the test is now successful although the time in the images is different.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WXM7UPdz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qrngytx9u4h9oq2wdamw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WXM7UPdz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qrngytx9u4h9oq2wdamw.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is great, you now have the possibility to only compare what you want in the image and ignore those items which will be different anyhow.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 Comparison With Small Difference Ignoring Area
&lt;/h3&gt;

&lt;p&gt;Besides a pattern, it is also possible to define an area to be ignored. Create a &lt;code&gt;masks/mask-bottom.json&lt;/code&gt; file containing the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"all"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Header Area"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"area"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bottom"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"percent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, you specify the bottom area and a percentage. The test case is now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| 04 | [Documentation] | Compare Two Images With Bottom Mask
|    | Compare Images  | images/base-image.png | images/base-image-with-triangle-difference.png | placeholder_file=masks/masks-bottom.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this test shows you that the bottom part of the image is ignored.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v75O6Csg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t3no2htijbm80jtkows9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v75O6Csg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t3no2htijbm80jtkows9.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 Comparison With Small Move
&lt;/h3&gt;

&lt;p&gt;When an item has moved slightly, the difference will be hard to spot. Besides the expected and actual result image, the Robot Framework log also shows us a combined result. The test is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| 05 | [Documentation] | Compare Two Images With Small Move
|    | Compare Images  | images/base-image.png | images/base-image-with-small-move.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the combined result you can see the difference much better.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rBBs0wD0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/up1c5schuj465q70w49v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rBBs0wD0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/up1c5schuj465q70w49v.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What if you would like to accept these kind of small moves? In other words, you do not want to fail the test when an item has moved a little bit. In that case, you can add the &lt;code&gt;move_tolerance&lt;/code&gt; argument and specify the amount of pixels you tolerate. The previous test becomes the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| 06 | [Documentation] | Compare Two Images With Small Move And Move Tolerance
|    | Compare Images  | images/base-image.png | images/base-image-with-small-move.png | move_tolerance=10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test now passes successfully and within the result, it is indicated that a small move has been found.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2C24cdLr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gntaf0a4pkm0zewk6hor.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2C24cdLr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gntaf0a4pkm0zewk6hor.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.5 PDF Compare With Small Difference
&lt;/h3&gt;

&lt;p&gt;Just like images, it is also possible to compare PDF files. This can be extremely useful when you have reports to compare. A small report is available in &lt;code&gt;pdf/base-report.pdf&lt;/code&gt; (the original &lt;code&gt;base-report.odt&lt;/code&gt; is also available). A report &lt;code&gt;pdf/base-report-with-small-difference.pdf&lt;/code&gt; contains a different amount for the screw driver and a different date. The test is similar as for the images, this time you provide the &lt;code&gt;get_pdf_content&lt;/code&gt; argument and set it to &lt;code&gt;true&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| 07 | [Documentation] | Compare Two PDF Files With Small Difference
|    | Compare Images  | pdf/base-report.pdf | pdf/base-report-with-small-difference.pdf | get_pdf_content=${true}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, the log file shows you exactly the difference:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4KLszqgN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xxymygwdep48vsurtm9v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4KLszqgN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xxymygwdep48vsurtm9v.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Masks also work with this kind of comparison. Let’s apply the date mask.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| 08 | [Documentation] | Compare Two PDF Files With Small Difference and Date Mask
|    | Compare Images  | pdf/base-report.pdf | pdf/base-report-with-small-difference.pdf | placeholder_file=masks/masks-date.json | get_pdf_content=${true}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As expected, only the amount difference is shown and the date is marked with blue indicating that it has been ignored.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pCBL2q2M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b27h9cngerboocpwbc3x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pCBL2q2M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b27h9cngerboocpwbc3x.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Conclusion
&lt;/h2&gt;

&lt;p&gt;The Robot Framework DocTest library is a really powerful library to compare differences in images and reports. It is simple in its use and it works just fine. This will definitely become a big time saver when you apply this during your testing efforts and it will reduce the chance for testing errors.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>tutorial</category>
      <category>news</category>
    </item>
    <item>
      <title>Automate ZAP With Docker</title>
      <dc:creator>mydeveloperplanet</dc:creator>
      <pubDate>Tue, 18 May 2021 18:14:12 +0000</pubDate>
      <link>https://dev.to/mydeveloperplanet/automate-zap-with-docker-4o53</link>
      <guid>https://dev.to/mydeveloperplanet/automate-zap-with-docker-4o53</guid>
      <description>&lt;p&gt;In the previous posts, you learned how to use ZAP with the Desktop client and via the command line with ZAP CLI. This post, you will learn how to use the Docker images which are provided by OWASP. This will even make it easier to automate ZAP, especially in a CI/CD pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;It is strongly advised to read the two previous posts about ZAP before starting with this one. You will need some files which were created in the previous posts. If you already have experience with ZAP, you can continue reading and use the files from the &lt;a href="https://github.com/mydeveloperplanet/MyZedAttackProxyPlanet" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; repository from directory &lt;code&gt;zap2docker&lt;/code&gt;. The generated reports will also be available in this repository. This way, you will be able to compare your results.&lt;/p&gt;

&lt;p&gt;In the previous posts, you were shown how to use the &lt;a href="https://mydeveloperplanet.com/2021/04/13/automated-pen-testing-with-zed-attack-proxy/" rel="noopener noreferrer"&gt;ZAP Desktop client&lt;/a&gt; and how to use &lt;a href="https://mydeveloperplanet.com/2021/04/28/automated-pen-testing-with-zap-cli/" rel="noopener noreferrer"&gt;ZAP CLI&lt;/a&gt; in order to automate the penetration test. However, OWASP also provides some &lt;a href="https://www.zaproxy.org/docs/docker/" rel="noopener noreferrer"&gt;Docker images&lt;/a&gt; which can be used for an automated scan.&lt;/p&gt;

&lt;p&gt;You will again use &lt;a href="https://owasp.org/www-project-webgoat/" rel="noopener noreferrer"&gt;WebGoat&lt;/a&gt; as vulnerable web application. If you followed the previous posts, it is better to start from scratch again and remove the Docker container you created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;rm &lt;/span&gt;goatandwolf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the application under test is running in a Docker container and ZAP will also run in a Docker container, it is necessary to create a Docker network. Otherwise it will not be possible to access WebGoat from within the ZAP Docker container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker network create zapnet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create the WebGoat container within the just created network &lt;code&gt;zapnet&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; goatandwolf &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="nt"&gt;-p&lt;/span&gt; 9090:9090 &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--net&lt;/span&gt; zapnet webgoat/goatandwolf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the &lt;a href="http://localhost:8080/WebGoat" rel="noopener noreferrer"&gt;WebGoat URL&lt;/a&gt; and create the user &lt;code&gt;mydeveloperplanet&lt;/code&gt; with password &lt;code&gt;password&lt;/code&gt;. This user will be used for authentication during the scan.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. ZAP Docker Full Scan
&lt;/h2&gt;

&lt;p&gt;The ZAP Docker image provides several scan possibilities. One of them is a &lt;strong&gt;Baseline Scan&lt;/strong&gt; which will scan your application passively. The active scan, however, will give you better results and this can be accomplished with the &lt;strong&gt;Full Scan&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You will need the IP address of WebGoat within the &lt;code&gt;zapnet&lt;/code&gt; network. This can be achieved with the following command. In the example below, &lt;code&gt;172.22.0.2&lt;/code&gt; is the IP address where WebGoat can be accessed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker inspect goatandwolf | &lt;span class="nb"&gt;grep &lt;/span&gt;IPAddress
            &lt;span class="s2"&gt;"SecondaryIPAddresses"&lt;/span&gt;: null,
            &lt;span class="s2"&gt;"IPAddress"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
                    &lt;span class="s2"&gt;"IPAddress"&lt;/span&gt;: &lt;span class="s2"&gt;"172.22.0.2"&lt;/span&gt;,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, you will scan the application without any user information. The complete list op options can be found &lt;a href="https://www.zaproxy.org/docs/docker/full-scan/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, below the used options are explained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--net&lt;/code&gt;: in order to add ZAP to the network together with WebGoat&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-v&lt;/code&gt;: this will map your current directory to the Docker image work directory&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-I&lt;/code&gt;: do not return failure on warning&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-j&lt;/code&gt;: run the AJAX spider in addition to the classic one&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-m 10&lt;/code&gt;: the number of minutes to spider for (just a safeguard, the spider takes less time than 10 minutes)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-T 60&lt;/code&gt;: limit the total scan to 60 minutes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-t&lt;/code&gt;: the URL to scan for&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-r&lt;/code&gt;: the name of the report for the results
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--net&lt;/span&gt; zapnet &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/zap/wrk/:rw &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; owasp/zap2docker-stable zap-full-scan.py &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="nt"&gt;-j&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 10 &lt;span class="nt"&gt;-T&lt;/span&gt; 60 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; http://172.22.0.2:8080/WebGoat &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-r&lt;/span&gt; 20210417-zap-full-scan-without-user.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just as you noticed when running the scan with ZAP CLI in the previous post, this scan will give you less results than expected. The spider does some work, but not enough and since you did not provide any user credentials, a large part of the application is not scanned.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp96hjtzj1sr0jewoqjs8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp96hjtzj1sr0jewoqjs8.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to provide the user credentials, you can provide the context &lt;code&gt;Webgoat.context&lt;/code&gt; you created last time. The only thing you need to do, is to replace &lt;code&gt;localhost&lt;/code&gt; with the IP address in the entire file. Move the context file to the current directory in order that it will be accessible in the ZAP work directory inside the Docker container. You add the following two extra options to the command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-n&lt;/code&gt;: The context file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-U&lt;/code&gt;: The user to use
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--net&lt;/span&gt; zapnet &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/zap/wrk/:rw &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; owasp/zap2docker-stable zap-full-scan.py &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="nt"&gt;-j&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 10 &lt;span class="nt"&gt;-T&lt;/span&gt; 60 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; http://172.22.0.2:8080/WebGoat &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-r&lt;/span&gt; 20210417-zap-full-scan.html &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; Webgoat.context &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-U&lt;/span&gt; mydeveloperplanet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this command, results in the following error. It states that the URL is not in the context, but it is. Even if this would work, it is doubtful whether the spider would have found all of the URLs of the application. You have noticed in the previous posts that a manual exploration of the website together with a spider gave much more URLs to scan.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;20432 &lt;span class="o"&gt;[&lt;/span&gt;ZAP-ProxyThread-11] WARN  org.zaproxy.zap.extension.api.API - Bad request to API endpoint &lt;span class="o"&gt;[&lt;/span&gt;/JSON/ajaxSpider/action/scanAsUser/] from &lt;span class="o"&gt;[&lt;/span&gt;127.0.0.1]:
org.zaproxy.zap.extension.api.ApiException: url_not_in_context
    at org.zaproxy.zap.extension.spiderAjax.AjaxSpiderAPI.startScan&lt;span class="o"&gt;(&lt;/span&gt;AjaxSpiderAPI.java:337&lt;span class="o"&gt;)&lt;/span&gt; ~[?:?]
    at org.zaproxy.zap.extension.spiderAjax.AjaxSpiderAPI.handleApiAction&lt;span class="o"&gt;(&lt;/span&gt;AjaxSpiderAPI.java:202&lt;span class="o"&gt;)&lt;/span&gt; ~[?:?]
    at org.zaproxy.zap.extension.api.API.handleApiRequest&lt;span class="o"&gt;(&lt;/span&gt;API.java:507&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;zap-2.10.0.jar:2.10.0]
    at org.parosproxy.paros.core.proxy.ProxyThread.processHttp&lt;span class="o"&gt;(&lt;/span&gt;ProxyThread.java:497&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;zap-2.10.0.jar:2.10.0]
    at org.parosproxy.paros.core.proxy.ProxyThread.run&lt;span class="o"&gt;(&lt;/span&gt;ProxyThread.java:333&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;zap-2.10.0.jar:2.10.0]
    at java.lang.Thread.run&lt;span class="o"&gt;(&lt;/span&gt;Thread.java:834&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;?:?]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. ICTU ZAP Docker Full Scan
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.ictu.nl/" rel="noopener noreferrer"&gt;ICTU&lt;/a&gt;, a Dutch IT organisation of the government has extended the ZAP Docker images with a webhook for authentication. It would be interesting to find out whether this way you can scan the application including authentication. Notice that the Docker image is now taken from the &lt;a href="https://hub.docker.com/r/ictu/zap2docker-weekly/" rel="noopener noreferrer"&gt;ICTU DockerHub&lt;/a&gt; page. Two extra options are added compared to the full scan without user authentication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--hook&lt;/code&gt;: the link to the Python script which will take care of the authentication&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-z&lt;/code&gt;: some extra parameters needed for the authentication
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/zap/wrk/:rw &lt;span class="nt"&gt;--net&lt;/span&gt; zapnet &lt;span class="nt"&gt;-t&lt;/span&gt; ictu/zap2docker-weekly zap-full-scan.py &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="nt"&gt;-j&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 10 &lt;span class="nt"&gt;-T&lt;/span&gt; 60 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; http://172.22.0.2:8080/WebGoat &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--hook&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/zap/auth_hook.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-r&lt;/span&gt; 20210417-ictuzap-full-scan.html &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"auth.loginurl=http://172.22.0.2:8080/WebGoat/login &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
      auth.username="&lt;/span&gt;mydeveloperplanet&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
      auth.password="&lt;/span&gt;password&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
      auth.auto=1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This seems to do its work. However, less results are found compared to the ZAP CLI scan. Most likely due to the spider again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F15twl41ubk8rdybjc8xe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F15twl41ubk8rdybjc8xe.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. ZAP CLI With Docker
&lt;/h2&gt;

&lt;p&gt;The good news is that ZAP CLI is also shipped in the ZAP Docker image. Good results were achieved with ZAP CLI, so let’s see whether this also applies when you run it from within the ZAP Docker container. You run the Docker container again with a volume mapping to your current directory and with option &lt;code&gt;-i&lt;/code&gt; in order to start the container in interactive mode. This will allow you to execute commands inside the Docker container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; zap-cli &lt;span class="nt"&gt;--net&lt;/span&gt; zapnet &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/zap/wrk/:rw &lt;span class="nt"&gt;-i&lt;/span&gt; owasp/zap2docker-stable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a test, you can verify whether WebGoat is accessible from within the ZAP Docker container with a &lt;code&gt;wget&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;wget http://172.22.0.2:8080/WebGoat
&lt;span class="nt"&gt;--2021-04-18&lt;/span&gt; 12:02:03--  http://172.22.0.2:8080/WebGoat
Connecting to 172.22.0.2:8080... connected.
HTTP request sent, awaiting response... 302 Found
Location: http://172.22.0.2:8080/WebGoat/ &lt;span class="o"&gt;[&lt;/span&gt;following]
&lt;span class="nt"&gt;--2021-04-18&lt;/span&gt; 12:02:03--  http://172.22.0.2:8080/WebGoat/
Reusing existing connection to 172.22.0.2:8080.
HTTP request sent, awaiting response... 302 Found
Location: http://172.22.0.2:8080/WebGoat/login &lt;span class="o"&gt;[&lt;/span&gt;following]
&lt;span class="nt"&gt;--2021-04-18&lt;/span&gt; 12:02:03--  http://172.22.0.2:8080/WebGoat/login
Reusing existing connection to 172.22.0.2:8080.
HTTP request sent, awaiting response... 200 OK
Length: unspecified &lt;span class="o"&gt;[&lt;/span&gt;text/html]
Saving to: ‘WebGoat’

     0K &lt;span class="nb"&gt;.&lt;/span&gt;                                                      94.3M&lt;span class="o"&gt;=&lt;/span&gt;0s

2021-04-18 12:02:03 &lt;span class="o"&gt;(&lt;/span&gt;94.3 MB/s&lt;span class="o"&gt;)&lt;/span&gt; - ‘WebGoat’ saved &lt;span class="o"&gt;[&lt;/span&gt;1877]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will follow the exact same steps as in the previous post. The only difference is that you will execute the commands from within the Docker container. First thing to do is to start ZAP. For simplicity, you will disable the API key. Remember that the API key was necessary to access the ZAP API. You can retrieve the API key if you want via the &lt;a href="https://www.zaproxy.org/docs/docker/webswing/" rel="noopener noreferrer"&gt;webswing ZAP UI&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nt"&gt;--log-path&lt;/span&gt; wrk/ start &lt;span class="nt"&gt;--start-options&lt;/span&gt; &lt;span class="s1"&gt;'-config api.disablekey=true'&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Starting ZAP daemon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import the context. Remember that you changed &lt;code&gt;localhost&lt;/code&gt; in the context file to the IP address where WebGoat can be accessed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nt"&gt;-v&lt;/span&gt; context import /zap/wrk/Webgoat.context
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Imported context from /zap/wrk/Webgoat.context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the previous post, you exported the manually explored URLs in a file &lt;code&gt;webgoat-exported-urls.txt&lt;/code&gt;. Copy this file to your current directory and find/replace localhost with the WebGoat IP address.&lt;/p&gt;

&lt;p&gt;Also, copy the &lt;code&gt;open-urls.sh&lt;/code&gt; script to your current directory and change the path to the text file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"wrk/webgoat-exported-urls.txt"&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; line
&lt;span class="k"&gt;do
  &lt;/span&gt;zap-cli open-url &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute the script, this will take approximately 10 minutes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./wrk/open-urls.sh
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Accessing URL http://172.22.0.2:8080
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Accessing URL http://172.22.0.2:8080/WebGoat
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Accessing URL http://172.22.0.2:8080/WebGoat/
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Accessing URL http://172.22.0.2:8080/WebGoat/AuthBypass.lesson.lesson
...
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Accessing URL http://172.22.0.2:8080/sitemap.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the classic spider.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nt"&gt;-v&lt;/span&gt; spider &lt;span class="nt"&gt;-c&lt;/span&gt; Webgoat &lt;span class="nt"&gt;-u&lt;/span&gt; mydeveloperplanet &lt;span class="s2"&gt;"http://172.22.0.2:8080/WebGoat"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Running spider...
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Spidering target http://172.22.0.2:8080/WebGoat...
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Running spider &lt;span class="k"&gt;in &lt;/span&gt;context 1 as user 2230
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Started spider with ID 0...
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Spider progress %: 0
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Spider &lt;span class="c"&gt;#0 completed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the active scan, this will take approximately 15 minutes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nt"&gt;-v&lt;/span&gt; active-scan &lt;span class="nt"&gt;--recursive&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; Webgoat &lt;span class="nt"&gt;-u&lt;/span&gt; mydeveloperplanet &lt;span class="s2"&gt;"http://172.22.0.2:8080/WebGoat"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Running an active scan...
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Scanning target http://172.22.0.2:8080/WebGoat...
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Scanning &lt;span class="k"&gt;in &lt;/span&gt;context 1 as user 2230
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Started scan with ID 0...
...
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Scan progress %: 98
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Scan &lt;span class="c"&gt;#0 completed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate the report.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nt"&gt;-v&lt;/span&gt; report &lt;span class="nt"&gt;-o&lt;/span&gt; /zap/wrk/report-zap-full-scan.html &lt;span class="nt"&gt;-f&lt;/span&gt; html
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Generating HTML report
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Report saved to &lt;span class="s2"&gt;"/zap/wrk/report-zap-full-scan.html"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, this gives you similar results as in the previous post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3ca5p2arion66sa90n4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3ca5p2arion66sa90n4.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save the session for next use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nt"&gt;-v&lt;/span&gt; session save /zap/wrk/webgoat-20210418-active-scan.session
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Saving the session to &lt;span class="s2"&gt;"/zap/wrk/webgoat-20210418-active-scan.session"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shutdown ZAP.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nt"&gt;-v&lt;/span&gt; shutdown
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Shutting down ZAP daemon
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Shutting down ZAP.
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           ZAP shutdown successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, type &lt;code&gt;exit&lt;/code&gt; to exit the interactive shell and shutdown the Webgoat Docker container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker stop goatandwolf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Conclusion
&lt;/h2&gt;

&lt;p&gt;It is great that OWASP provides Docker images with ZAP pre-installed. This simplifies installation and makes it easier to integrate it into your CI/CD pipeline. The default scans which are provided did not give good enough results. Luckily, ZAP CLI is also provided and this did the job. Also note that ZAP CLI will be replaced in the near future with the &lt;a href="https://www.zaproxy.org/docs/automate/automation-framework/" rel="noopener noreferrer"&gt;Automation Framework&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>testing</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Automated Pen Testing With ZAP CLI</title>
      <dc:creator>mydeveloperplanet</dc:creator>
      <pubDate>Wed, 05 May 2021 18:45:00 +0000</pubDate>
      <link>https://dev.to/mydeveloperplanet/automated-pen-testing-with-zap-cli-3jkd</link>
      <guid>https://dev.to/mydeveloperplanet/automated-pen-testing-with-zap-cli-3jkd</guid>
      <description>&lt;p&gt;In the previous post, you learnt how to execute an automated penetration test by means of Zed Attack Proxy (ZAP). This time, you will learn how to execute the test via a Command Line Interface (CLI) which will make it possible to add the test to your CI/CD pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://mydeveloperplanet.com/2021/04/13/automated-pen-testing-with-zed-attack-proxy/" rel="noopener noreferrer"&gt;previous post&lt;/a&gt;, the different steps were explained how to execute an automated penetration test. The application under test being used was WebGoat, a vulnerable application developed by OWASP in order to learn security vulnerabilities. This application will be used in this post also. The steps to be executed for a penetration test with ZAP are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup the context and session (especially login credentials when a part of the application is behind a login);&lt;/li&gt;
&lt;li&gt;Manually explore all parts of the application;&lt;/li&gt;
&lt;li&gt;Spider the application;&lt;/li&gt;
&lt;li&gt;Execute the automated scan;&lt;/li&gt;
&lt;li&gt;Inspect the results.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beware that an automated scan will not find all vulnerabilities! It is always necessary to execute a manual penetration test. The automated scan will however give you a good indication about the state of security of your application.&lt;/p&gt;

&lt;p&gt;The files being used in this post are available at &lt;a href="https://github.com/mydeveloperplanet/MyZedAttackProxyPlanet" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; in directory &lt;code&gt;zap-cli&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. ZAP CLI
&lt;/h2&gt;

&lt;p&gt;ZAP contains an API for controlling ZAP. The &lt;a href="https://github.com/Grunny/zap-cli" rel="noopener noreferrer"&gt;ZAP CLI&lt;/a&gt; tool is a tool which wraps the API in order that commands can be executed via the command line. In this section, you basically will perform the same or similar actions as in the previous post, except that you will not use the ZAP Desktop this time. A complete list of the commands of ZAP CLI can be found at the GitHub website.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 Installation and Preparation
&lt;/h3&gt;

&lt;p&gt;As a prerequisite, you must have ZAP already installed of course. Next to ZAP, you need to install ZAP CLI, which is quite simple. Just execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; zapcli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before you continue, it is very important to set the following environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ZAP_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8090
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ZAP_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/bin/zap.sh
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ZAP_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your API key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ZAP_API_KEY can be found in ZAP Desktop. Therefore, start ZAP Desktop and choose &lt;strong&gt;&lt;em&gt;Tools – Options…&lt;/em&gt;&lt;/strong&gt; in the menu. In the &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt; section, the API key is shown and needs to be used for the environment variable (but do not yet set the environment variable until it is mentioned to do so in the next section). You also have the possibility to disable the usage of the key, but this is not advised.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywxx11dn7hc10cpamnp9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywxx11dn7hc10cpamnp9.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, close the ZAP Desktop tool before you continue. Let’s see what happens when you try to start ZAP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli start
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Starting ZAP daemon
Traceback &lt;span class="o"&gt;(&lt;/span&gt;most recent call last&lt;span class="o"&gt;)&lt;/span&gt;:
  File &lt;span class="s2"&gt;"/home/&amp;lt;user&amp;gt;/.local/bin/zap-cli"&lt;/span&gt;, line 8, &lt;span class="k"&gt;in&lt;/span&gt; &amp;lt;module&amp;gt;
    sys.exit&lt;span class="o"&gt;(&lt;/span&gt;cli&lt;span class="o"&gt;())&lt;/span&gt;
  File &lt;span class="s2"&gt;"/home/&amp;lt;user&amp;gt;/.local/lib/python3.8/site-packages/click/core.py"&lt;/span&gt;, line 829, &lt;span class="k"&gt;in &lt;/span&gt;__call__
    &lt;span class="k"&gt;return &lt;/span&gt;self.main&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;args, &lt;span class="k"&gt;**&lt;/span&gt;kwargs&lt;span class="o"&gt;)&lt;/span&gt;
  File &lt;span class="s2"&gt;"/home/&amp;lt;user&amp;gt;/.local/lib/python3.8/site-packages/click/core.py"&lt;/span&gt;, line 782, &lt;span class="k"&gt;in &lt;/span&gt;main
    rv &lt;span class="o"&gt;=&lt;/span&gt; self.invoke&lt;span class="o"&gt;(&lt;/span&gt;ctx&lt;span class="o"&gt;)&lt;/span&gt;
  File &lt;span class="s2"&gt;"/home/&amp;lt;user&amp;gt;/.local/lib/python3.8/site-packages/click/core.py"&lt;/span&gt;, line 1259, &lt;span class="k"&gt;in &lt;/span&gt;invoke
    &lt;span class="k"&gt;return &lt;/span&gt;_process_result&lt;span class="o"&gt;(&lt;/span&gt;sub_ctx.command.invoke&lt;span class="o"&gt;(&lt;/span&gt;sub_ctx&lt;span class="o"&gt;))&lt;/span&gt;
  File &lt;span class="s2"&gt;"/home/&amp;lt;user&amp;gt;/.local/lib/python3.8/site-packages/click/core.py"&lt;/span&gt;, line 1066, &lt;span class="k"&gt;in &lt;/span&gt;invoke
    &lt;span class="k"&gt;return &lt;/span&gt;ctx.invoke&lt;span class="o"&gt;(&lt;/span&gt;self.callback, &lt;span class="k"&gt;**&lt;/span&gt;ctx.params&lt;span class="o"&gt;)&lt;/span&gt;
  File &lt;span class="s2"&gt;"/home/&amp;lt;user&amp;gt;/.local/lib/python3.8/site-packages/click/core.py"&lt;/span&gt;, line 610, &lt;span class="k"&gt;in &lt;/span&gt;invoke
    &lt;span class="k"&gt;return &lt;/span&gt;callback&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;args, &lt;span class="k"&gt;**&lt;/span&gt;kwargs&lt;span class="o"&gt;)&lt;/span&gt;
  File &lt;span class="s2"&gt;"/home/&amp;lt;user&amp;gt;/.local/lib/python3.8/site-packages/click/decorators.py"&lt;/span&gt;, line 33, &lt;span class="k"&gt;in &lt;/span&gt;new_func
    &lt;span class="k"&gt;return &lt;/span&gt;f&lt;span class="o"&gt;(&lt;/span&gt;get_current_context&lt;span class="o"&gt;()&lt;/span&gt;.obj, &lt;span class="k"&gt;*&lt;/span&gt;args, &lt;span class="k"&gt;**&lt;/span&gt;kwargs&lt;span class="o"&gt;)&lt;/span&gt;
  File &lt;span class="s2"&gt;"/home/&amp;lt;user&amp;gt;/.local/lib/python3.8/site-packages/zapcli/cli.py"&lt;/span&gt;, line 58, &lt;span class="k"&gt;in &lt;/span&gt;start_zap_daemon
    zap_helper.start&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;start_options&lt;span class="o"&gt;)&lt;/span&gt;
  File &lt;span class="s2"&gt;"/home/&amp;lt;user&amp;gt;/.local/lib/python3.8/site-packages/zapcli/zap_helper.py"&lt;/span&gt;, line 88, &lt;span class="k"&gt;in &lt;/span&gt;start
    with open&lt;span class="o"&gt;(&lt;/span&gt;log_path, &lt;span class="s1"&gt;'w+'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; as log_file:
PermissionError: &lt;span class="o"&gt;[&lt;/span&gt;Errno 13] Permission denied: &lt;span class="s1"&gt;'/usr/local/bin/zap.log'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It seems that you do not have sufficient rights to write to the &lt;code&gt;zap.log&lt;/code&gt;. There are several solutions for this, but the easiest one is to set the log path to a directory where you have sufficient permissions. Navigate to this directory and execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nt"&gt;--log-path&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; start
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Starting ZAP daemon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This seems to have started the ZAP daemon. Let’s verify this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli status
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            ZAP is running
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Allright, ZAP is running. Let’s try to shutdown the ZAP daemon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli shutdown
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Shutting down ZAP daemon
...
    raise ProxyError&lt;span class="o"&gt;(&lt;/span&gt;e, &lt;span class="nv"&gt;request&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;request&lt;span class="o"&gt;)&lt;/span&gt;
requests.exceptions.ProxyError: HTTPConnectionPool&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;, &lt;span class="nv"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8090&lt;span class="o"&gt;)&lt;/span&gt;: Max retries exceeded with url: http://zap/JSON/core/action/shutdown/?apikey&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Caused by ProxyError&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Cannot connect to proxy.'&lt;/span&gt;, RemoteDisconnected&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Remote end closed connection without response'&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why is this not working? The reason for the exception is quite vague because it mentions ‘Cannot connect to proxy’ and ‘Remote end closed connection without response’. But why? The answer is shown in the preceding URL &lt;code&gt;http://zap/JSON/core/action/shutdown/?apikey=&lt;/code&gt;. As you can see, the parameter &lt;code&gt;apikey&lt;/code&gt; is empty. There are numerous questions on the internet about this exception, but very few will give you the answer. The solution is to set the ZAP_API_KEY environment variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ZAP_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your API key&amp;gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nb"&gt;.&lt;/span&gt; shutdown
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Shutting down ZAP daemon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now it is possible to shut down the ZAP daemon. It appears that you do not need the API key for every API call, but when you set the environment variables, you will not need to worry about this.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Context Preparation
&lt;/h3&gt;

&lt;p&gt;Remember from the previous post, that it was important to set user credentials in the Context and the manual exploration of the website. In this post, you will reuse this information in order to feed it to ZAP CLI.&lt;/p&gt;

&lt;p&gt;Open the previously stored session via &lt;strong&gt;&lt;em&gt;File – Open Session…&lt;/em&gt;&lt;/strong&gt; and export the context via &lt;strong&gt;&lt;em&gt;File – Export Context…&lt;/em&gt;&lt;/strong&gt; Select the context, the path where you want to save it and click the &lt;strong&gt;&lt;em&gt;Save&lt;/em&gt;&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5uvu08o84dap9b8rlf00.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5uvu08o84dap9b8rlf00.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The manually explored URLs of the application are also present in the Session. You will need those URLs for ZAP CLI. Right-click in the &lt;strong&gt;&lt;em&gt;Sites&lt;/em&gt;&lt;/strong&gt; section on &lt;a href="http://localhost:8080/" rel="noopener noreferrer"&gt;http://localhost:8080/&lt;/a&gt; and choose &lt;strong&gt;&lt;em&gt;Export Selected URLs to File…&lt;/em&gt;&lt;/strong&gt; and name the file &lt;code&gt;webgoat-exported-urls.txt&lt;/code&gt;. You will need this file in the next paragraph.&lt;/p&gt;

&lt;p&gt;Close ZAP Desktop.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.3 Scan With ZAP CLI
&lt;/h3&gt;

&lt;p&gt;At this moment, you have done all the necessary preparation in order to get started to scan your application with ZAP CLI. Identical steps as during the ZAP Desktop scan will be performed, but this time via the ZAP CLI and you will make use of the Context and the exported URLs which have been created in the previous post.&lt;/p&gt;

&lt;p&gt;Before you continue, ensure that the WebGoat application is running. Otherwise, it is a good moment to start the application now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker start goatandwolf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the ZAP daemon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nt"&gt;--log-path&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; start
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Starting ZAP daemon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import the context. Ensure to include the complete path to the context file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nt"&gt;-v&lt;/span&gt; context import /&amp;lt;path&amp;gt;/Webgoat.context 
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Imported context from /&amp;lt;path&amp;gt;/Webgoat.context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you list the contexts, you will notice that the list seems to be empty, but it isn’t. It is probably a bug that the name is not correctly displayed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli context list
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Available contexts: &lt;span class="o"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next step is to explore the website, you will use the exported URLs file you created in the previous paragraph. What you will do, is to open each URL which is present in this file with the &lt;code&gt;zap-cli open-url&lt;/code&gt; command. Create a bash script &lt;code&gt;open-urls.sh&lt;/code&gt; for this with the following contents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"webgoat-exported-urls.txt"&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; line
&lt;span class="k"&gt;do
  &lt;/span&gt;zap-cli open-url &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Give the bash script executable permissions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x open-urls.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute the bash script, this can take some time dependent on how many URLs you have in the text file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./open-urls.sh
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Accessing URL http://localhost:8080
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Accessing URL http://localhost:8080/WebGoat
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the spider. Notice that the context is provided and also the user &lt;code&gt;mydeveloperplanet&lt;/code&gt; which will allow you to login to the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nt"&gt;-v&lt;/span&gt; spider &lt;span class="nt"&gt;-c&lt;/span&gt; Webgoat &lt;span class="nt"&gt;-u&lt;/span&gt; mydeveloperplanet &lt;span class="s2"&gt;"http://localhost:8080/WebGoat"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Running spider...
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Spidering target http://localhost:8080/WebGoat...
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Running spider &lt;span class="k"&gt;in &lt;/span&gt;context 1 as user 2230
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Started spider with ID 0...
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Spider progress %: 0
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Spider &lt;span class="c"&gt;#0 completed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not run the AJAX spider. It is good to note that with the argument &lt;code&gt;--help&lt;/code&gt;, you can view the command arguments available for this command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli ajax-spider &lt;span class="nt"&gt;--help&lt;/span&gt;
Usage: zap-cli ajax-spider &lt;span class="o"&gt;[&lt;/span&gt;OPTIONS] URL

  Run the AJAX Spider against a URL.

Options:
  &lt;span class="nt"&gt;--help&lt;/span&gt;  Show this message and exit.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, only the URL is available as a parameter. This is strange because in the Desktop version it is possible to provide a context and a user. It also seems that it negatively influences the end results (less alerts were reported). Therefore, do not run the AJAX spider.&lt;/p&gt;

&lt;p&gt;Start the active scan. This will take some time (about 10 to 15 minutes).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nt"&gt;-v&lt;/span&gt; active-scan &lt;span class="nt"&gt;--recursive&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; Webgoat &lt;span class="nt"&gt;-u&lt;/span&gt; mydeveloperplanet &lt;span class="s2"&gt;"http://localhost:8080/WebGoat"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Running an active scan...
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Scanning target http://localhost:8080/WebGoat...
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Scanning &lt;span class="k"&gt;in &lt;/span&gt;context 1 as user 2230
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Started scan with ID 0...
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Scan progress %: 0
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Scan progress %: 1
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Scan progress %: 3
...
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Scan progress %: 97
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Scan progress %: 99
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Scan &lt;span class="c"&gt;#0 completed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate the report.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nt"&gt;-v&lt;/span&gt; report &lt;span class="nt"&gt;-o&lt;/span&gt; /&amp;lt;path&amp;gt;/report-zap-cli-first-scan.html &lt;span class="nt"&gt;-f&lt;/span&gt; html
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Generating HTML report
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Report saved to &lt;span class="s2"&gt;"/&amp;lt;path&amp;gt;/report-zap-cli-first-scan.html"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the session. This will allow you to open it in ZAP Desktop for example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nt"&gt;-v&lt;/span&gt; session save /&amp;lt;path&amp;gt;/webgoat-20210410-active-scan.session
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Saving the session to &lt;span class="s2"&gt;"/&amp;lt;path&amp;gt;/webgoat-20210410-active-scan.session"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And shutdown the ZAP daemon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zap-cli &lt;span class="nt"&gt;-v&lt;/span&gt; shutdown
&lt;span class="o"&gt;[&lt;/span&gt;INFO]            Shutting down ZAP daemon
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           Shutting down ZAP.
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG]           ZAP shutdown successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Conclusion
&lt;/h2&gt;

&lt;p&gt;ZAP CLI gives you the opportunity to execute an automated penetration test via the command line. This will make it also possible to include it into your CI/CD pipeline. Beware that it will give you only an indication about the security of your application and it does not replace a complete penetration test. Also note that the results are not identical to the results from the previous post. But when you execute the same steps as in this post starting from a clean session in ZAP Desktop, the results will be quite similar. Reason for the difference is probably the execution of the AJAX spider or the manually exploration of the website from within ZAP Desktop.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>security</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Automated Pen Testing With Zed Attack Proxy</title>
      <dc:creator>mydeveloperplanet</dc:creator>
      <pubDate>Wed, 21 Apr 2021 17:24:42 +0000</pubDate>
      <link>https://dev.to/mydeveloperplanet/automated-pen-testing-with-zed-attack-proxy-2l94</link>
      <guid>https://dev.to/mydeveloperplanet/automated-pen-testing-with-zed-attack-proxy-2l94</guid>
      <description>&lt;p&gt;In this post, you will learn how to execute penetration tests with OWASP Zed Attack Proxy (ZAP). ZAP is a free web app scanner which can be used for security testing purposes.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;When you are developing an application, security must be addressed. It cannot be ignored anymore nowadays. Security must be taken into account starting from initial development and not thinking about it when you want to deploy to production for the first time. Often you will notice that adding security to your application at a later stage in development, will take a lot of time. It is better to take security into account from the beginning, this will save you from some painful headaches. You probably have some security experts inside of your company, so let them participate from the start when a new application needs to be developed. Nevertheless, you will also need to verify whether your developed application is secure. Penetration tests can help you with that. &lt;a href="https://www.zaproxy.org/" rel="noopener noreferrer"&gt;OWASP Zed Attack Proxy&lt;/a&gt; (ZAP) is a tool which can help you execute penetration tests for your application. In this post, you will learn how to setup ZAP and execute tests with the desktop client of ZAP. You will also need a preferably vulnerable application. For this purposes, &lt;a href="https://owasp.org/www-project-webgoat/" rel="noopener noreferrer"&gt;Webgoat&lt;/a&gt; of OWASP will be used. In case you do not know what Webgoat is, you can read a &lt;a href="https://mydeveloperplanet.com/2019/11/27/hack-the-owasp-goat/" rel="noopener noreferrer"&gt;previous post&lt;/a&gt; first. It might be a little bit outdated because Webgoat has been improved since then, but it will give you a good impression of what Webgoat is. It is advised to disconnect from the internet when using Webgoat because it may expose your machine to attacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Start the Application
&lt;/h2&gt;

&lt;p&gt;First thing to do, is to start Webgoat. The easiest way is to run it as a Docker container. The Docker image contains the applications Webgoat and Webwolf, but you will only use Webgoat in this post. You give the container the name &lt;code&gt;goatandwolf&lt;/code&gt; (this will make it easy to start and stop the container) and you run it in detached mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; goatandwolf &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="nt"&gt;-p&lt;/span&gt; 9090:9090 &lt;span class="nt"&gt;-d&lt;/span&gt; webgoat/goatandwolf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the container has started, verify whether you can access Webgoat via the browser at URL &lt;a href="http://localhost:8080/WebGoat/" rel="noopener noreferrer"&gt;http://localhost:8080/WebGoat/&lt;/a&gt;. The login page should be shown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff49zta2u4loiw86sm461.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff49zta2u4loiw86sm461.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s it for now, you are ready now to start with ZAP.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Zed Attack Proxy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Installation
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.zaproxy.org/download/" rel="noopener noreferrer"&gt;Installation instructions&lt;/a&gt; for ZAP are dependent of your OS. For Linux, you download the file &lt;code&gt;ZAP_2_10_0_unix.sh&lt;/code&gt; and execute it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./ZAP_2_10_0_unix.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start ZAP, leave the default persistence setting and click &lt;strong&gt;Start&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F41ug3zv540ef45z701md.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F41ug3zv540ef45z701md.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Quick Start Scan
&lt;/h3&gt;

&lt;p&gt;The quickest way to start a scan is to use the &lt;strong&gt;Quick Start&lt;/strong&gt; menu and start an automated scan. Click the &lt;strong&gt;Automated scan&lt;/strong&gt; button in this menu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhq28lx18jbcmsgrj4uv9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhq28lx18jbcmsgrj4uv9.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill in the URL you want to attack, enable &lt;strong&gt;Use ajax spider&lt;/strong&gt; and click the &lt;strong&gt;Attack&lt;/strong&gt; button. Do not think too much about all the options at this moment, they will become more clear later on in this post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F193nddka8e4o9xrtgcaq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F193nddka8e4o9xrtgcaq.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some interesting things can be noted after running the scan. Let’s take a look at the &lt;strong&gt;Sites&lt;/strong&gt; section and unfold it so you can see which URL’s did participate to the scan.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5lq1m3wkggaw4wcciywa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5lq1m3wkggaw4wcciywa.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The spiders try to explore your website and do find some useful things, but in this case, almost your entire website is located after a login page. The scan was executed without logging in, so the major part of your website is not scanned. The scan did find some alerts but not as many as expected.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fjcqmqal3h6eorr7fqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fjcqmqal3h6eorr7fqj.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The automated scan is a nice way for obtaining some quick results, but nothing more than that. It can definitely not be seen as a good scan of your application, certainly when the larger part of your application needs a login.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 Explore Your Application
&lt;/h3&gt;

&lt;p&gt;One way or the other, you will need to let ZAP know how your application looks like. So, you need to manually explore your website and click all links, buttons, fill in all available forms, even navigate to maybe hidden URL’s, etc. You need to do so for every role your application has, in the case of WebGoat, you will only explore the site for a regular user in this post. Let’s start doing so!&lt;/p&gt;

&lt;p&gt;Go to the &lt;strong&gt;Quick Start&lt;/strong&gt; menu again, this time choose &lt;strong&gt;Manual Explore&lt;/strong&gt;. Fill in the URL if not already done and click the &lt;strong&gt;Launch Browser&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2c06cbbpy4a5g0wiepd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2c06cbbpy4a5g0wiepd.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A browser window is opened and the login page is shown, just as we saw before when we accessed WebGoat. The difference is, that ZAP is now in between the browser and the application which makes it possible for ZAP to intercept all traffic and follow what we are doing onto the website.&lt;/p&gt;

&lt;p&gt;Let’s continue with creating a user. Click the &lt;strong&gt;Register new user&lt;/strong&gt; link and create a user. In the example below, the user &lt;code&gt;mydeveloperplanet&lt;/code&gt; with password &lt;code&gt;password&lt;/code&gt; (the password must be between 6 and 10 characters) is created. Agree with the terms and conditions and click the &lt;strong&gt;Sign up&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqkcszref8ayvywfwnoqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqkcszref8ayvywfwnoqp.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it is time for the most tedious work, you have to explore the website as good as possible. When you do so, you will notice that the &lt;strong&gt;Sites&lt;/strong&gt; section will continue to grow with new URL’s.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpm15ejba2duq7kroraow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpm15ejba2duq7kroraow.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Close the browser and save this session via &lt;strong&gt;File – Persist Session…&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 Create Context
&lt;/h3&gt;

&lt;p&gt;Before you can continue, you must provide login information to ZAP which it can use during scanning. This information has to be stored in the context. Double-click &lt;strong&gt;Default Context&lt;/strong&gt; in the Sites section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftp3kfvayxaqcw069nyjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftp3kfvayxaqcw069nyjq.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Session Properties&lt;/strong&gt; window opens where settings concerning the session can be changed. You can for example change the session name to a more convenient one than &lt;strong&gt;Untitled Session&lt;/strong&gt;. You name it for example &lt;code&gt;WebGoat mydeveloperplanet&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5s1x9w4bi7oyl7fscrli.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5s1x9w4bi7oyl7fscrli.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More importantly is the &lt;strong&gt;Contexts&lt;/strong&gt; section. Change the name &lt;strong&gt;Default Context&lt;/strong&gt; to e.g. &lt;code&gt;Webgoat&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8zdi4e4x9xq725fq3eej.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8zdi4e4x9xq725fq3eej.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the WebGoat website and underlying URL’s by means of a regular expression to the &lt;strong&gt;Include in Context&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubouvtun12h1j0ip1uqw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubouvtun12h1j0ip1uqw.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Authentication&lt;/strong&gt; section, choose &lt;strong&gt;Form-based Authentication&lt;/strong&gt; (this will be dependent of your application) and fill in the URL for the login page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07k24gst3sysbnggh33x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07k24gst3sysbnggh33x.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And finally, add the created user in the &lt;strong&gt;Users&lt;/strong&gt; section. Fill in the user name and password as previously created. This will allow ZAP to login to the application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0ckkfnz06rlvqx2woej.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0ckkfnz06rlvqx2woej.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do not forget to click the &lt;strong&gt;OK&lt;/strong&gt; button in order to save the changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.5 ZAP Modes
&lt;/h3&gt;

&lt;p&gt;ZAP can act in four different modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Safe&lt;/strong&gt;: no potentially dangerous operations are permitted;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protected&lt;/strong&gt;: only perform potentially dangerous operations on URL’s which are in scope;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard&lt;/strong&gt;: you can do anything;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ATTACK&lt;/strong&gt;: new nodes that are in scope are actively scanned as soon as they are discovered.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You must be aware of the fact that you can only run ZAP against a website you own. It is therefore recommended that you use Protected mode to ensure that you only attack your own sites.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwyadyvt3r1v3lu448xp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwyadyvt3r1v3lu448xp.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.6 Start Spider
&lt;/h3&gt;

&lt;p&gt;Although you have done your best clicking on every link, button, you can think of, a spider can help you to reveal the things you might have missed. You can run a traditional spider and/or an AJAX spider. The latter will also crawl dynamically built links. Remember that you also enabled these spiders during the Quickscan.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Sites&lt;/strong&gt; section, right-click the WebGoat URL and choose &lt;strong&gt;Attack – Spider…&lt;/strong&gt;. Select the user &lt;code&gt;mydeveloperplanet&lt;/code&gt; and click the &lt;strong&gt;Start Scan&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffjtz7ad15mz2sgb425wj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffjtz7ad15mz2sgb425wj.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Spider finishes quickly and in the results you will notice that a certain amount of nodes have been added (37 in the screenshot).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fymmxtm4dmmordebc5vb3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fymmxtm4dmmordebc5vb3.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do the same with the AJAX spider. Right-click the WebGoat URL and choose &lt;strong&gt;Attack – AJAX Spider…&lt;/strong&gt;, fill the necessary items and click the &lt;strong&gt;Start Scan&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6kb6e5omu2na4untd7lw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6kb6e5omu2na4untd7lw.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again some new URL’s have been added.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.7 Active Scan
&lt;/h3&gt;

&lt;p&gt;At this moment, you have done all the preparation work and now it is time for the real action. Right-click the WebGoat URL, select &lt;strong&gt;Attack – Active Scan…&lt;/strong&gt;, fill the necessary items and click the &lt;strong&gt;Start Scan&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwsoeo8mwjqy9w39jhws.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwsoeo8mwjqy9w39jhws.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ZAP will do its work now, so be patient, this can take some time (it took approximately 15 minutes for the scan to complete). At the end, the results can be viewed in the &lt;strong&gt;Alerts&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmogfpvk5g6g01zo4w4ll.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmogfpvk5g6g01zo4w4ll.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking an alert will show more information about the vulnerability and how to solve it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8p1aslu8v47gnqnwmeq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8p1aslu8v47gnqnwmeq.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.8 Export Results
&lt;/h3&gt;

&lt;p&gt;Several export functions are available in the &lt;strong&gt;Reports&lt;/strong&gt; menu for generating reports. You can for example generate a HTML report with &lt;strong&gt;Generate HTML Report…&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07e53ncqayqs8buxw1jx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07e53ncqayqs8buxw1jx.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Next Steps
&lt;/h2&gt;

&lt;p&gt;The active scan will give you a first indication about vulnerabilities. Beware however that the active scan can only find certain types of vulnerabilities. In addition to the active scan, manual penetration tests should always be performed. The &lt;a href="https://owasp.org/www-project-top-ten/" rel="noopener noreferrer"&gt;OWASP Top 10&lt;/a&gt; website gives you good information about whether a vulnerability can be found with an automated scan or not.&lt;/p&gt;

&lt;p&gt;In addition to the active scan, you can also use the &lt;strong&gt;Forced Browse Site&lt;/strong&gt; which will try to find undiscoverable links and the &lt;strong&gt;Fuzz&lt;/strong&gt; which will send in random data to your site.&lt;/p&gt;

&lt;p&gt;Do not forget to stop the WebGoat Docker container when you are finished testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker stop goatandwolf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog you learnt how to use Zed Attack Proxy. It is advised to experiment with it, try to solve the issues, check which other information is available in the tool in order to get more acquainted with it. It is for example also possible to intercept a request and change items in the request.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>security</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Improve Your Robot Framework Tests With Robocop</title>
      <dc:creator>mydeveloperplanet</dc:creator>
      <pubDate>Tue, 06 Apr 2021 17:24:52 +0000</pubDate>
      <link>https://dev.to/mydeveloperplanet/improve-your-robot-framework-tests-with-robocop-38p0</link>
      <guid>https://dev.to/mydeveloperplanet/improve-your-robot-framework-tests-with-robocop-38p0</guid>
      <description>&lt;p&gt;Testing software will tell you something about the quality of your software. But how do you ensure that your tests have a high quality? A static analysis tool can help you in order to improve the quality. Robocop is a such a static analysis tool for Robot Framework tests. In this blog, you will learn how to use and configure Robocop for your purposes.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;Static code analyzers are commonly used during software development. SonarQube is a popular tool used for this purposes. The intention is to scan the code and verify whether it complies to a certain set of rules. The rules to check against should be agreed upon within your company and serve as best practices. The purpose of the scan is to learn, to standardize, to improve readability, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/robotframework-robocop/"&gt;Robocop&lt;/a&gt; is such a static analysis tool for &lt;a href="https://robotframework.org/"&gt;Robot Framework&lt;/a&gt; tests. In case you need a basic overview of the capabilities of Robot Framework, you can read some previous blogs as an introduction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mydeveloperplanet.com/2020/05/19/automated-acceptance-testing-with-robot-framework/"&gt;Automated Acceptance Testing With Robot Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mydeveloperplanet.com/2020/06/02/how-to-write-data-driven-tests-with-robot-framework/"&gt;How to Write Data Driven Tests With Robot Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mydeveloperplanet.com/2020/06/16/create-custom-robot-framework-libraries/"&gt;Create Custom Robot Framework Libraries&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Robocop &lt;a href="https://robocop.readthedocs.io/en/latest/"&gt;documentation&lt;/a&gt; gives you a good overview of all capabilities. In the sections below, you will learn how to use the tool from a practical point of view. This way, it should be quite easy for you to apply it to your own Robot Framework test cases. As an example, you can use the &lt;a href="https://github.com/mydeveloperplanet/MyRobotFrameworkPlanet"&gt;GitHub&lt;/a&gt; repository from the previous blogs which contain a set of test cases and resource files. The &lt;code&gt;master&lt;/code&gt; branch contains non-analyzed tests, the &lt;code&gt;feature/robocop&lt;/code&gt; branch contains fixes for the Robocop warnings.&lt;/p&gt;

&lt;p&gt;First of all, you will need to install Robocop. At the time of writing, Robocop v1.5 is the latest release.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;robotframework-robocop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Run a Scan
&lt;/h2&gt;

&lt;p&gt;Let’s see what happens when you run a Robocop scan from within the root of the repository. Robocop will search for test cases and resource files in the current directory and in the subdirectories and will run with default settings. The output shows you all the files where violations against the rules have been found. The output format gives you the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The file name where the violation has been found;&lt;/li&gt;
&lt;li&gt;The line number where the violation occured;&lt;/li&gt;
&lt;li&gt;The column where the violation has been detected;&lt;/li&gt;
&lt;li&gt;The severity of the violation (E for error, W for warning, I for information);&lt;/li&gt;
&lt;li&gt;The ID of the rule;&lt;/li&gt;
&lt;li&gt;The description of the rule.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;robocop
&amp;lt;path&amp;gt;/test/employee.robot:1:0 &lt;span class="o"&gt;[&lt;/span&gt;W] 0704 Ignored data found &lt;span class="k"&gt;in &lt;/span&gt;file
&amp;lt;path&amp;gt;/test/employee.robot:4:0 &lt;span class="o"&gt;[&lt;/span&gt;W] 1003 Invalid number of empty lines between sections &lt;span class="o"&gt;(&lt;/span&gt;1/2&lt;span class="o"&gt;)&lt;/span&gt;
&amp;lt;path&amp;gt;/test/employee.robot:7:0 &lt;span class="o"&gt;[&lt;/span&gt;W] 1003 Invalid number of empty lines between sections &lt;span class="o"&gt;(&lt;/span&gt;1/2&lt;span class="o"&gt;)&lt;/span&gt;
&amp;lt;path&amp;gt;/test/employee.robot:9:13 &lt;span class="o"&gt;[&lt;/span&gt;W] 1007 Line is over-indented
&amp;lt;path&amp;gt;/test/employee.robot:11:15 &lt;span class="o"&gt;[&lt;/span&gt;W] 0906 Redundant equal sign &lt;span class="k"&gt;in &lt;/span&gt;variable assignment
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to limit the number of violations, it is possible to limit the scan to one specific file. This way, only the results for this particular file will be shown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;robocop &lt;span class="nb"&gt;test&lt;/span&gt;/employee.robot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Robocop also provides the option to generate a report of the issues found. It will give you a summary which provides a nice overview.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;robocop &lt;span class="nt"&gt;--report&lt;/span&gt; all &lt;span class="nb"&gt;test&lt;/span&gt;/employee.robot
&amp;lt;path&amp;gt;/test/employee.robot:1:0 &lt;span class="o"&gt;[&lt;/span&gt;W] 0704 Ignored data found &lt;span class="k"&gt;in &lt;/span&gt;file
...
Processed 1 file&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; from which 1 file&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; contained issues

Issues by IDs:
W1007 &lt;span class="o"&gt;(&lt;/span&gt;uneven-indent&lt;span class="o"&gt;)&lt;/span&gt;                : 6
W0906 &lt;span class="o"&gt;(&lt;/span&gt;redundant-equal-sign&lt;span class="o"&gt;)&lt;/span&gt;         : 5
W1003 &lt;span class="o"&gt;(&lt;/span&gt;empty-lines-between-sections&lt;span class="o"&gt;)&lt;/span&gt; : 3
W0302 &lt;span class="o"&gt;(&lt;/span&gt;not-capitalized-keyword-name&lt;span class="o"&gt;)&lt;/span&gt; : 3
W0704 &lt;span class="o"&gt;(&lt;/span&gt;ignored-data&lt;span class="o"&gt;)&lt;/span&gt;                 : 1
W0508 &lt;span class="o"&gt;(&lt;/span&gt;line-too-long&lt;span class="o"&gt;)&lt;/span&gt;                : 1
W1002 &lt;span class="o"&gt;(&lt;/span&gt;missing-trailing-blank-line&lt;span class="o"&gt;)&lt;/span&gt;  : 1

Found 20 issue&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;: 20 WARNING&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

Scan took 0.006s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. More About the Rules
&lt;/h2&gt;

&lt;p&gt;You now have an overview of the issues being found, but what are the rules being checked against? This is well documented in the &lt;a href="https://robocop.readthedocs.io/en/latest/checkers.html"&gt;official&lt;/a&gt; documentation. Another way of showing this list, is by means of the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;robocop &lt;span class="nt"&gt;--list&lt;/span&gt;
Rule - 0201 &lt;span class="o"&gt;[&lt;/span&gt;W]: missing-doc-keyword: Missing documentation &lt;span class="k"&gt;in &lt;/span&gt;keyword &lt;span class="o"&gt;(&lt;/span&gt;enabled&lt;span class="o"&gt;)&lt;/span&gt;
Rule - 0202 &lt;span class="o"&gt;[&lt;/span&gt;W]: missing-doc-testcase: Missing documentation &lt;span class="k"&gt;in &lt;/span&gt;&lt;span class="nb"&gt;test &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;enabled&lt;span class="p"&gt;)&lt;/span&gt;
Rule - 0203 &lt;span class="o"&gt;[&lt;/span&gt;W]: missing-doc-suite: Missing documentation &lt;span class="k"&gt;in &lt;/span&gt;suite &lt;span class="o"&gt;(&lt;/span&gt;enabled&lt;span class="o"&gt;)&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It might be the case that you do not agree with the severity of a certain rule. Let us assume that you do not agree with the severity of the &lt;code&gt;line-too-long&lt;/code&gt; rule. This rule has a severity &lt;strong&gt;Warning&lt;/strong&gt;, but you want to change it to severity &lt;strong&gt;Information&lt;/strong&gt;. With Robocop it is possible to change this behaviour by means of the configuration argument. You just need to use the &lt;strong&gt;ID&lt;/strong&gt; or &lt;strong&gt;message name&lt;/strong&gt; of the rule, followed by argument &lt;code&gt;severity&lt;/code&gt;, followed by the new value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;robocop &lt;span class="nt"&gt;-c&lt;/span&gt; line-too-long:severity:i &lt;span class="nt"&gt;--report&lt;/span&gt; all &lt;span class="nb"&gt;test&lt;/span&gt;/employee.robot
...
&amp;lt;path&amp;gt;/test/employee.robot:34:0 &lt;span class="o"&gt;[&lt;/span&gt;I] 0508 Line is too long &lt;span class="o"&gt;(&lt;/span&gt;138/120&lt;span class="o"&gt;)&lt;/span&gt;
...
Issues by IDs:
...
I0508 &lt;span class="o"&gt;(&lt;/span&gt;line-too-long&lt;span class="o"&gt;)&lt;/span&gt;                : 1
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the output you can now see that the issue is still reported, but now with severity &lt;strong&gt;Information&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Not only the severity can be changed. A lot of rules have configurable parameters which can be changed according to your preferences. In order to know what these parameters are, you can take a look at the documentation. But it is also possible to show this information by means of the &lt;code&gt;list-configurables&lt;/code&gt; command followed by the &lt;strong&gt;ID&lt;/strong&gt; or &lt;strong&gt;message name&lt;/strong&gt; of the rule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;robocop &lt;span class="nt"&gt;--list-configurables&lt;/span&gt; line-too-long
Rule - 0508 &lt;span class="o"&gt;[&lt;/span&gt;W]: line-too-long: Line is too long &lt;span class="o"&gt;(&lt;/span&gt;%d/%d&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;enabled&lt;span class="o"&gt;)&lt;/span&gt;
    Available configurable&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;this rule:
        severity
        line_length
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Besides the severity, it is also possible to change the line length of the &lt;code&gt;line-too-long&lt;/code&gt; rule. Just like the severity, you change this parameter to e.g. 160. Running the scan with this setting, no issue is raised now because the line length is within the limits you specified.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;robocop &lt;span class="nt"&gt;-c&lt;/span&gt; line-too-long:line_length:160 &lt;span class="nt"&gt;--report&lt;/span&gt; all &lt;span class="nb"&gt;test&lt;/span&gt;/employee.robot
...
Found 19 issue&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;: 19 WARNING&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, one issue less is shown in the total number of issues and in your output, no &lt;code&gt;line-too-long&lt;/code&gt; warning is shown anymore.&lt;/p&gt;

&lt;p&gt;It might be that you do not agree with a certain rule at all. In this case, you can disable a rule from being checked. You can do so by means of the &lt;code&gt;exclude&lt;/code&gt; argument. Again, this will result in 19 warnings for the &lt;code&gt;employee.robot&lt;/code&gt; test case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;robocop &lt;span class="nt"&gt;--exclude&lt;/span&gt; 0508 &lt;span class="nt"&gt;--report&lt;/span&gt; all &lt;span class="nb"&gt;test&lt;/span&gt;/employee.robot
...
Found 19 issue&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;: 19 WARNING&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Listing the rules, you notice that rule &lt;code&gt;0508&lt;/code&gt; is now indicated as being disabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;robocop &lt;span class="nt"&gt;--exclude&lt;/span&gt; 0508 &lt;span class="nt"&gt;--list&lt;/span&gt;
...
Rule - 0508 &lt;span class="o"&gt;[&lt;/span&gt;W]: line-too-long: Line is too long &lt;span class="o"&gt;(&lt;/span&gt;%d/%d&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;disabled&lt;span class="o"&gt;)&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In another case, you might want the rule to be enabled by default, but you want to disable an issue. You have noticed the issue and you are ok with it and do not want to be triggered about this part again in the future. You can disable a certain part of your test case for a specific rule. It is not advised to use this regularly, it will not improve the readability of your test case. It is better to fix the issue or to disable the rule or to reconfigure the rule for your needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# robocop: disable=line-too-long
| | ${rc}                       | ${output} =     | Run and Return RC and Output | ${APPLICATION} add_employee ${first_name} ${last_name}
# robocop: enable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Use a Configuration File
&lt;/h2&gt;

&lt;p&gt;Instead of tweaking the configuration of rules in the command line, you can add the configuration in a configuration file. This way you can ensure that everyone is using the same configuration, certainly when you put it under version control. The contents of this file is for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--exclude 0508
--report all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we run robocop with the &lt;code&gt;argumentfile&lt;/code&gt; argument followed by the file containing the arguments, &lt;code&gt;robocop_args.txt&lt;/code&gt; in this case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;robocop &lt;span class="nt"&gt;--argumentfile&lt;/span&gt; robocop_args.txt  &lt;span class="nb"&gt;test&lt;/span&gt;/employee.robot 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Robocop also offers the possibility to read the configuration file by default. This is restricted under the following conditions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The argument file must be named &lt;code&gt;.robocop&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Robocop must be run without any other arguments. Note that it may not contain any other argument, also no file to analyze for example. It only works when only the &lt;code&gt;robocop&lt;/code&gt; command is being used.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;.robocop&lt;/code&gt; argument file has the following content when you follow the above example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--exclude 0508
--report all
test/employee.robot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it is sufficient to use the &lt;code&gt;robocop&lt;/code&gt; command. In the output you can see that the default configuration file has been loaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;robocop 
Loaded default configuration file from &lt;span class="s1"&gt;'&amp;lt;path&amp;gt;/.robocop'&lt;/span&gt;
Processed 1 file&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; from which 1 file&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; contained issues

Issues by IDs:
No issues found

Found 0 issues

Scan took 0.005s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Fix Issues
&lt;/h2&gt;

&lt;p&gt;In this section, some experiences are shared when fixing the issues in the test cases in the repository.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;employee.robot&lt;/code&gt; file, all lines containing a &lt;code&gt;Documentation&lt;/code&gt; tag raise the over-indented warning.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;employee.robot:11:4 &lt;span class="o"&gt;[&lt;/span&gt;W] 1007 Line is over-indented
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tried a lot of things in order to fix this, but this was not successful. Raised an &lt;a href="https://github.com/MarketSquare/robotframework-robocop/issues/284"&gt;issue&lt;/a&gt; for this and received a very quick response from the Robocop maintainers. It seems that the rule is ok, but when fixing it, another warning is shown (under-indented warning). This last one is not correct, but is already fixed in a Pull Request and will be available in a next release.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;data_driver.robot&lt;/code&gt; file, a warning is raised about a missing documentation in the test case. In this case, it is ok to disable the rule, because this is the way the Data Driver library works. You cannot fix this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*** Test Cases ***
# robocop: disable=missing-doc-testcase
| Add Employee ${first_name} ${last_name}
# robocop: enable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before starting using Robocop, take a good look at the rules you want to enable or disable. Not every rule will be suitable for you and it is better to only enable those rules you really want to check upon. When too many rules are enabled which do not increase your overall quality, but are reported every time, people will tend to not using it anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Conclusion
&lt;/h2&gt;

&lt;p&gt;Robocop is a great initiative which can be of good help when you want to increase the quality of your Robot Framework scripts. Beware of which rules you enable, you do not want to be overflooded by warnings you do not care about.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>testing</category>
      <category>codequality</category>
      <category>devops</category>
    </item>
    <item>
      <title>Why Start a Technical Blog</title>
      <dc:creator>mydeveloperplanet</dc:creator>
      <pubDate>Tue, 23 Mar 2021 18:33:50 +0000</pubDate>
      <link>https://dev.to/mydeveloperplanet/why-start-a-technical-blog-4o3</link>
      <guid>https://dev.to/mydeveloperplanet/why-start-a-technical-blog-4o3</guid>
      <description>&lt;p&gt;About 3,5 years ago, I started this blog. Why have I done this and has it brought to me what I expected it to be. In this blog, I will look back at the past 3,5 years and share my experiences. It will also containe some tips and tricks when you want to start blogging yourself. Enjoy!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Beginning
&lt;/h2&gt;

&lt;p&gt;Since the start of my career, I have always been interested in learning new things. About 15 years ago, I learnt myself programming in Java and wanted to find a job as a Java software developer. It was difficult, because I almost always got rejected because of lack of experience. In those days, I started my first website where I would write about the books I read, the small applications I wrote, etc. This has definitely helped me getting my first Java job. For numerous reasons, I quit maintaining my website although I did keep on reading books, exploring new technologies, etc.&lt;/p&gt;

&lt;p&gt;It occured to me, that in conversations with colleagues, I often could tell that I already did something with a certain piece of technology. However, it also often happened that I did not know anymore how I did something because I did not write it down. Also, sometimes I could recall how things needed to be done, but then I needed to write it down in an email, which costed me extra time and only the one colleague I mailed it to, had the benefit of it. At such a moment, 3,5 years ago, I thought about my first tech website, about sharing knowledge to more than 1 person, about writing down things I learnt in order to remember better how I accomplished things, and so on. That was the moment this blog was born!&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Why
&lt;/h2&gt;

&lt;p&gt;In everything you do, it is important to know why you are doing it (Remember the Golden Circle of Simon Sinek). So, I first thought about what I wanted to accomplish with my blog. The most important thing for me was to learn new things. I was already doing that of course, but it was not very steady. I spent effort during some weekends and then I would stop again for some weeks. Sometimes spending a lot of time finding out where I left off the last time. A blog and a promise to myself for posting every x weeks, should solve that problem. Another motivation is that I like to share knowledge. If I spend time writing something down, I will in the first place remember what I have done and secondly, someone else can also take benefit of it. To conclude, I have three major reasons which will keep me motivated for writing a blog:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learn new things;&lt;/li&gt;
&lt;li&gt;Have a more steady commitment;&lt;/li&gt;
&lt;li&gt;Share knowledge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I do not think that a motivator should be to earn money with your blog. It would merely be a side effect, but it should not be one of your main drivers. However, you can find a lot of articles on the internet about this topic. There are other motivators which can be of significant importance, but which I consider also as nice-to-have side effects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Appreciation&lt;/strong&gt;: I do not merely mean Likes but rather personal comments. People who thank you for writing a blog or find it useful. This is a great motivator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Number of visitors&lt;/strong&gt;: an increasing number of visitors to your website is also motivating. However, I do not know how to judge this because it changes rapidly or slowly without knowing the cause of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visibility&lt;/strong&gt;: it certainly increases your visibility outside of your company. I am sure that opportunities will pass by more easily.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. The How
&lt;/h2&gt;

&lt;p&gt;Let us assume that you got motivated yourself for writing a blog. How do you get started? I can explain how I started and my advice is: just start. Below some tips and tricks how to get started.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Where to Post
&lt;/h3&gt;

&lt;p&gt;You need a website where you can post your blog. The best option is to have your own website and your own domain name. When you post to your own website, your blogs will always be your content and you do not need to give away the ownership of what you have written. An own domain name will be better for search results and it will be easier to move your website to another provider when necessary. I did not want to spend a lot of time for maintaining the website and I chose to publish at &lt;a href="https://wordpress.com/"&gt;WordPress&lt;/a&gt;. I started with the &lt;strong&gt;Free Plan&lt;/strong&gt; but I shifted some time ago to the &lt;strong&gt;Personal Plan&lt;/strong&gt;. It costs a small amount of money and in return you can disable advertisements and you receive customer support, which, in my experience, has been good up till now. I started also with acquiring a domain name, but switched to another registrar later on. I did not know that my personal information was published publicly at the first registrar I used. This resulted in an increasing amount of spam to my mail account. So, take a registrar where your personal details can be kept anonymous for the main public (the so called &lt;code&gt;whois&lt;/code&gt; information).&lt;/p&gt;

&lt;p&gt;If you do not want to maintain your own website, then you can choose websites where you can post your blogs. I can recommend &lt;a href="https://dzone.com/"&gt;DZone&lt;/a&gt; and &lt;a href="https://dev.to/"&gt;dev.to&lt;/a&gt;. Medium is also a popular website for blogs, but your content cannot be published again outside of &lt;a href="https://medium.com/"&gt;Medium&lt;/a&gt;. So for me, this is a no-go. I also publish at DZone and dev.to, but I will explain this in one of the next sections.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 How to Attrack Visitors
&lt;/h3&gt;

&lt;p&gt;We definitely want visitors to our website. We do want our blogs to be read by as many people as possible. Once you created your website, it must be found one way or the other, otherwise you will not have very much visitors. I must admit that this is not my strongest skill. However, I have gained some experience with it the past years.&lt;/p&gt;

&lt;p&gt;The most easiest way is to share it via social media. Whenever you have a new blog, post it at LinkedIn, Twitter, … whatever medium you want. Do not expect a large increase in visitors when you have posted something, but it will increase your visibility and people will get to know your website.&lt;/p&gt;

&lt;p&gt;Most of the visitors to my blog are originated by Google’s search results. Thus, it is important that your articles get ranked high in Google’s search results. You need therefore clicks, other websites linking to yours, etc. Links from other websites is one of the reasons I also publish at DZone and dev.to. But ensure that you set the &lt;a href="https://en.wikipedia.org/wiki/Canonical_link_element"&gt;canonical&lt;/a&gt; URL to your website. For both blog platforms, you have the possibility to set the canonical URL. You will find opinions that you should not publish at other locations because they will take away traffic from your website. I have done an experiment by not publishing anymore at other websites and after a few months, traffic to my website drastically decreased. After starting publishing again, it increased again and it all had to do with Google’s search results. Besides that, you will reach other people that learn to know your website.&lt;/p&gt;

&lt;p&gt;Search for newsletters which are created by others and ask to include a link to your blog.&lt;/p&gt;

&lt;p&gt;There are probably many other things you can do, but as said before, I am not an expert in website marketing.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 Writing Style
&lt;/h3&gt;

&lt;p&gt;Which writing style should you use? I have experimented with several writing styles and I am still searching and trying to improve it. When it is a tutorial like one, I try to use the second person (you) in the blog. When it is more an opinion, I try to make it more personal and will use the first person (I) in the blog. A good starting point for writing technical documentation, blogs can be found at &lt;a href="https://github.com/rubymorillo/pocket-tech-writing-list"&gt;this&lt;/a&gt; GitHub page. The style guides can give you some tips. You can take a look at the style guides, read a part of it now and then and improve your writing skills ongoing.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The What
&lt;/h2&gt;

&lt;p&gt;So, what are you going to write about? Do not think too much about it whether you have something valuable or useful to write about, just write and share it. I have noticed that in a lot of blogs, assumptions are made about the knowledge of the reader. That should be ok when it is an in-depth blog, but for getting started guides, it is often disappointing when someone writes some vague text what you should do without a step-by-step guide. E.g. someone writes to startup a Docker container without writing down the command which you should use.&lt;/p&gt;

&lt;p&gt;I try to create a mixed content of what I already know and what I would like to learn. Investigating and experimenting with something new costs a significant amount of time and sometimes things just do not work from the first time. Besides that, I want to decide myself of what I am going to write about, otherwise I will not stay motivated. I write in my spare time, so it must not become a task to do it, it must be fun.&lt;/p&gt;

&lt;p&gt;At the start, you probably can come up with quite some topics to write about. But at a certain moment, inspiration can become a problem. I follow people at Twitter, read other blogs, websites, watch tech talks, attend conferences, etc. Whenever I read something interesting, I write it down in a todo list. Sometimes it is sufficient information to write a blog right away, most often it is the starting point for reading more about the topic first before I can write about it. I generally have no problem finding a topic, my todo list contains currently about 40 topics. The list is alive, topics get on the list, sometimes topics get off the list without writing a blog about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Some More Tips
&lt;/h2&gt;

&lt;p&gt;In this last section, I will list some tips which I want to share but did not really fit in one of the previous sections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Publish at a &lt;strong&gt;fixed interval&lt;/strong&gt;. One that suits you. I originally started with a one week interval and noticed that this was too optimistic. I quickly changed it to a two week fixed interval.&lt;/li&gt;
&lt;li&gt;Take some &lt;strong&gt;time off&lt;/strong&gt;. I take two breaks each year. One break in July and August and one break from mid December up to mid January. It allows me to do some other things and to work in advance. I try to have two finished blogs into the pipeline.&lt;/li&gt;
&lt;li&gt;Decide how much &lt;strong&gt;time&lt;/strong&gt; you want to spend to your blog. I do not keep time when writing a blog, but I think it takes me 8 to 16 hours for writing a blog, dependent on the topic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog, I wanted to share my experiences about maintaining a blog. I do hope it will help someone to get started or that someone gets inspired by it.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>devops</category>
      <category>learning</category>
      <category>writing</category>
    </item>
    <item>
      <title>How to Monitor a Spring Boot App</title>
      <dc:creator>mydeveloperplanet</dc:creator>
      <pubDate>Wed, 10 Mar 2021 18:39:44 +0000</pubDate>
      <link>https://dev.to/mydeveloperplanet/how-to-monitor-a-spring-boot-app-f4m</link>
      <guid>https://dev.to/mydeveloperplanet/how-to-monitor-a-spring-boot-app-f4m</guid>
      <description>&lt;p&gt;In this blog you will learn how to monitor a Spring Boot application. You will make use of Spring Actuator, Micrometer, Prometheus and Grafana. Seems a lot of work, but this is easier as you might think!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;When an application runs in production (but also your other environments), it is wise to monitor its health. You want to make sure that everything is running without any problems and the only way to know this, is to measure the health of your application. When something goes wrong, you hopefully will be notified before your customer notices the problem and maybe you can solve the problem before your customer notices anything. In this post, you will create a sample Spring Boot application which you can monitor with the help of Spring Actuator, Micrometer, Prometheus and Grafana. This is visualized in the overview below, where Spring Actuator and Micrometer are part of the Spring Boot App.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkccl9jnb3wbg8u7wt5du.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkccl9jnb3wbg8u7wt5du.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The purpose of the different components is explained briefly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spring Actuator&lt;/strong&gt;: supplies several endpoints in order to monitor and interact with your application. See &lt;a href="https://mydeveloperplanet.com/2018/04/18/spring-boot-actuator-in-spring-boot-2-0/" rel="noopener noreferrer"&gt;Spring Boot Actuator in Spring Boot 2.0&lt;/a&gt; for more information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Micrometer&lt;/strong&gt;: an application metrics facade that supports numerous monitoring systems, Spring Boot Actuator provides support for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus&lt;/strong&gt;: a timeseries database in order to collect the metrics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grafana&lt;/strong&gt;: a dashboard for displaying the metrics.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every component will be covered in the next sections. The code used in this post can be found at &lt;a href="https://github.com/mydeveloperplanet/myspringmetricsplanet" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Create Sample App
&lt;/h2&gt;

&lt;p&gt;First thing to do is to create a sample application which can be monitored. Go to &lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;Spring Initializr&lt;/a&gt;, add dependency &lt;code&gt;Spring Boot Actuator&lt;/code&gt;, &lt;code&gt;Prometheus&lt;/code&gt; and &lt;code&gt;Spring Web&lt;/code&gt;. The sample application will be a Spring MVC application with two dummy endpoints.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;RestController&lt;/code&gt; with the two endpoints. The endpoints only return a simple &lt;code&gt;String&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MetricsController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/endPoint1"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;endPoint1&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Metrics for endPoint1"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/endPoint2"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;endPoint2&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Metrics for endPoint2"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mvn spring-boot:run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the endpoints are working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:8080/endPoint1
Metrics &lt;span class="k"&gt;for &lt;/span&gt;endPoint1
&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:8080/endPoint2
Metrics &lt;span class="k"&gt;for &lt;/span&gt;endPoint2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the Spring Actuator endpoint. The endpoint returns the information in json. In order to format the response so that it is readable, you can pipe the output of the actuator endpoint to &lt;code&gt;mjson&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:8080/actuator | python &lt;span class="nt"&gt;-mjson&lt;/span&gt;.tool
...
&lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="s2"&gt;"_links"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"self"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;
         &lt;span class="s2"&gt;"href"&lt;/span&gt;:&lt;span class="s2"&gt;"http://localhost:8080/actuator"&lt;/span&gt;,
         &lt;span class="s2"&gt;"templated"&lt;/span&gt;:false
      &lt;span class="o"&gt;}&lt;/span&gt;,
      &lt;span class="s2"&gt;"health"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;
         &lt;span class="s2"&gt;"href"&lt;/span&gt;:&lt;span class="s2"&gt;"http://localhost:8080/actuator/health"&lt;/span&gt;,
         &lt;span class="s2"&gt;"templated"&lt;/span&gt;:false
      &lt;span class="o"&gt;}&lt;/span&gt;,
      &lt;span class="s2"&gt;"health-path"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;
         &lt;span class="s2"&gt;"href"&lt;/span&gt;:&lt;span class="s2"&gt;"http://localhost:8080/actuator/health/{*path}"&lt;/span&gt;,
         &lt;span class="s2"&gt;"templated"&lt;/span&gt;:true
      &lt;span class="o"&gt;}&lt;/span&gt;,
      &lt;span class="s2"&gt;"info"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;
         &lt;span class="s2"&gt;"href"&lt;/span&gt;:&lt;span class="s2"&gt;"http://localhost:8080/actuator/info"&lt;/span&gt;,
         &lt;span class="s2"&gt;"templated"&lt;/span&gt;:false
      &lt;span class="o"&gt;}&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, the above information is available. Much more information can be provided by Spring Actuator, but you need to enable this. In order to enable the Prometheus endpoint, you need to add the following line into the &lt;code&gt;application.properties&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;management.endpoints.web.exposure.include=health,info,prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart the application and retrieve the data from the Prometheus endpoint. A large bunch of metrics are returned and available. Only a small part of the output is displayed because it is a really long list. The information which is available at this endpoint, will be used by Prometheus.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:8080/actuator/prometheus
&lt;span class="c"&gt;# HELP jvm_gc_pause_seconds Time spent in GC pause&lt;/span&gt;
&lt;span class="c"&gt;# TYPE jvm_gc_pause_seconds summary&lt;/span&gt;
jvm_gc_pause_seconds_count&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"end of minor GC"&lt;/span&gt;,cause&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"G1 Evacuation Pause"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt; 2.0
jvm_gc_pause_seconds_sum&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"end of minor GC"&lt;/span&gt;,cause&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"G1 Evacuation Pause"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt; 0.009
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As mentioned before, &lt;a href="https://micrometer.io/" rel="noopener noreferrer"&gt;Micrometer&lt;/a&gt; is also needed. Micrometer can be seen as SLF4J, but then for metrics. Spring Boot Actuator provides autoconfiguration for Micrometer. The only thing you need to do is to have a dependency on &lt;code&gt;micrometer-registry-{system}&lt;/code&gt; in your runtime classpath and that is exactly what we did by adding the &lt;code&gt;prometheus&lt;/code&gt; dependency when creating the Spring Boot app.&lt;/p&gt;

&lt;p&gt;The metrics Actuator endpoint can also be accessed when you add it to the &lt;code&gt;application.properties&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;management.endpoints.web.exposure.include=health,info,metrics,prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart the application and retrieve the data from the metrics endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:8080/actuator/metrics | python &lt;span class="nt"&gt;-mjson&lt;/span&gt;.tool
...
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"names"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"http.server.requests"&lt;/span&gt;,
        &lt;span class="s2"&gt;"jvm.buffer.count"&lt;/span&gt;,
        &lt;span class="s2"&gt;"jvm.buffer.memory.used"&lt;/span&gt;,
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each individual metric can be retrieved by adding it to the URL. E.g. the &lt;code&gt;http.server.requests&lt;/code&gt; parameter can be retrieved as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:8080/actuator/metrics/http.server.requests | python &lt;span class="nt"&gt;-mjson&lt;/span&gt;.tool
...
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"http.server.requests"&lt;/span&gt;,
    &lt;span class="s2"&gt;"description"&lt;/span&gt;: null,
    &lt;span class="s2"&gt;"baseUnit"&lt;/span&gt;: &lt;span class="s2"&gt;"seconds"&lt;/span&gt;,
    &lt;span class="s2"&gt;"measurements"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"statistic"&lt;/span&gt;: &lt;span class="s2"&gt;"COUNT"&lt;/span&gt;,
            &lt;span class="s2"&gt;"value"&lt;/span&gt;: 3.0
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"statistic"&lt;/span&gt;: &lt;span class="s2"&gt;"TOTAL_TIME"&lt;/span&gt;,
            &lt;span class="s2"&gt;"value"&lt;/span&gt;: 0.08918682
        &lt;span class="o"&gt;}&lt;/span&gt;,
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Add Prometheus
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://prometheus.io/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt; is an open source monitoring system of the Cloud Native Computing Foundation. Since you have an endpoint in your application which provides the metrics for Prometheus, you can now configure Prometheus to monitor your Spring Boot application. The Spring documentation for doing so can be found &lt;a href="https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready-metrics-export-prometheus" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are several ways to install Prometheus as described in the &lt;a href="https://prometheus.io/docs/prometheus/latest/installation/" rel="noopener noreferrer"&gt;installation&lt;/a&gt; section of the Prometheus documentation. In this section, you will run Prometheus inside a Docker container.&lt;/p&gt;

&lt;p&gt;You need to create a &lt;a href="https://prometheus.io/docs/prometheus/latest/configuration/configuration/" rel="noopener noreferrer"&gt;configuration&lt;/a&gt; &lt;code&gt;prometheus.yml&lt;/code&gt; file with a basic configuration to add to the Docker container. The minimal properties are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;scrape_interval&lt;/code&gt;: how often Prometheus polls the metrics endpoint of your application&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;job_name&lt;/code&gt;: just a name for the polling job&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;metrics_path&lt;/code&gt;: the path to the URL where the metrics can be accessed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;targets&lt;/code&gt;: the hostname and port number. Replace &lt;code&gt;HOST&lt;/code&gt; with the IP address of your host machine
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scrape_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;     &lt;span class="s"&gt;15s&lt;/span&gt;

&lt;span class="na"&gt;scrape_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;myspringmetricsplanet'&lt;/span&gt;
    &lt;span class="na"&gt;metrics_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/actuator/prometheus'&lt;/span&gt;
    &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;HOST:8080'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have difficulties finding out your IP address on Linux, you can use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ip &lt;span class="nt"&gt;-f&lt;/span&gt; inet &lt;span class="nt"&gt;-o&lt;/span&gt; addr show docker0 | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $4}'&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the docker container and bind-mount the local &lt;code&gt;prometheus.yml&lt;/code&gt; file to the one in the docker container. The above &lt;code&gt;prometheus.yml&lt;/code&gt; file can be found in the git repository in directory &lt;code&gt;prometheus&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; 9090:9090 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-v&lt;/span&gt; /path/to/prometheus.yml:/etc/prometheus/prometheus.yml &lt;span class="se"&gt;\&lt;/span&gt;
    prom/prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After successful startup of the Docker container, first verify whether Prometheus is able to gather the data via url &lt;a href="http://localhost:9090/targets" rel="noopener noreferrer"&gt;http://localhost:9090/targets&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6dh4efugt3cdr7dz4pi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6dh4efugt3cdr7dz4pi.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It seems that Prometheus is not able to access the Spring Boot application running on the host. An error &lt;code&gt;context deadline exceeded&lt;/code&gt; is mentioned.&lt;/p&gt;

&lt;p&gt;This error can be solved by adding the Docker container to your host network which will enable Prometheus to access the URL. Therefore, add &lt;code&gt;--network host&lt;/code&gt; as a parameter. Also remove the port mapping as this has no effect when &lt;code&gt;--network&lt;/code&gt; is being used. Finally, give your container a name, this will make it easier to start and stop the container. The &lt;code&gt;-d&lt;/code&gt; parameter will run the container in detached mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; prometheus &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--network&lt;/span&gt; host &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-v&lt;/span&gt; /path/to/prometheus.yml:/etc/prometheus/prometheus.yml &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    prom/prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify again the Prometheus targets URL, the state indicates UP which means the prerequisite of accessing the metrics endpoint is fullfilled now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flzemji9ayvbmvablb863.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flzemji9ayvbmvablb863.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is now possible to display the Prometheus metrics. Navigate to &lt;a href="http://localhost:9090/graph" rel="noopener noreferrer"&gt;http://localhost:9090/graph&lt;/a&gt;, enter &lt;code&gt;http_server_requests_seconds_max&lt;/code&gt; in the search box and click the Execute button. Access a couple of times the &lt;code&gt;endPoint1&lt;/code&gt; URL in order to generate some traffic. This parameter will give you the maximum execution time during a time period of a request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkxbzi3mhhtjqowontye1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkxbzi3mhhtjqowontye1.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Add Grafana
&lt;/h2&gt;

&lt;p&gt;The last component to add is &lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt;. Although Prometheus is able to display the metrics, Grafana will allow you to show the metrics in a more fancy dashboard. Grafana also supports several ways for installing it, but you will run it in a &lt;a href="https://grafana.com/docs/grafana/latest/installation/docker/" rel="noopener noreferrer"&gt;Docker container&lt;/a&gt;, just like you did with Prometheus.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; grafana &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 grafana/grafana
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to URL &lt;a href="http://localhost:3000/" rel="noopener noreferrer"&gt;http://localhost:3000/&lt;/a&gt;, the URL where Grafana is accessible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hw0g85swqv4lia6xrcn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hw0g85swqv4lia6xrcn.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The default username/password is admin/admin. After clicking the &lt;strong&gt;Log in&lt;/strong&gt; button, you need to change the default password. Google Chrome will also warn you about the default username/password.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44k5gynye62bonr4d0qm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44k5gynye62bonr4d0qm.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next thing to do, is to add a &lt;strong&gt;Data Source&lt;/strong&gt;. Click in the left sidebar the &lt;strong&gt;Configuration&lt;/strong&gt; icon and select &lt;strong&gt;Data Sources&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1fzokm7wv1z4to70ft1z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1fzokm7wv1z4to70ft1z.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;Add data source&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5bs39i97smoafc951794.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5bs39i97smoafc951794.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prometheus is at the top of the list, select &lt;strong&gt;Prometheus&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F79k32kz9epv4p5h34g93.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F79k32kz9epv4p5h34g93.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill the &lt;strong&gt;URL&lt;/strong&gt; where Prometheus can be accessed, set &lt;strong&gt;HTTP Access&lt;/strong&gt; to &lt;strong&gt;Browser&lt;/strong&gt; and click the &lt;strong&gt;Save &amp;amp; Test&lt;/strong&gt; button at the bottom of the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdd662fds0yi3hqintgte.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdd662fds0yi3hqintgte.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When everything is OK, a green notification banner is shown indicating that the data source is working.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8hhxa07d7fuuip5xosv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8hhxa07d7fuuip5xosv.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it is time to create a dashboard. You can create one of your own, but there are also several dashboards available which you can use. A popular one for displaying the Spring Boot metrics is the &lt;a href="https://grafana.com/grafana/dashboards/4701" rel="noopener noreferrer"&gt;JVM dashboard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the left sidebar, click the + sign and choose &lt;strong&gt;Import&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqgufsezxeuxrgq9jw4wm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqgufsezxeuxrgq9jw4wm.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter the URL &lt;a href="https://grafana.com/grafana/dashboards/4701" rel="noopener noreferrer"&gt;https://grafana.com/grafana/dashboards/4701&lt;/a&gt; where the JVM dashboard can be found and click the &lt;strong&gt;Load&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgk3ff2bivfvjbolo0tfo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgk3ff2bivfvjbolo0tfo.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter a meaningful name for the dashboard (e.g. &lt;strong&gt;MySpringMonitoringPlanet&lt;/strong&gt;), select &lt;strong&gt;Prometheus&lt;/strong&gt; as Data Source and click the &lt;strong&gt;Import&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F15h20xc4qtcip70x77jx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F15h20xc4qtcip70x77jx.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this moment, you have a cool first Grafana dashboard at your disposal. Do not forget to scroll down, there are more metrics than shown in the screenshot. The default range is set to 24 hours, this is maybe a bit large when you just started the application. You can change the range in the top right corner. Change it to f.e. &lt;strong&gt;Last 30 minutes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftwoymlohl3u4cpovjaqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftwoymlohl3u4cpovjaqj.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is also possible to add a custom panel to the dashboard. At the top of the dashboard, click the &lt;strong&gt;Add panel&lt;/strong&gt; icon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1e9g33iir0gb8p8g2n7k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1e9g33iir0gb8p8g2n7k.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Add new panel&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5meya2dd20qjmgvgpuqe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5meya2dd20qjmgvgpuqe.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Metrics&lt;/strong&gt; field, you enter &lt;strong&gt;http_server_requests_seconds_max&lt;/strong&gt;, in the &lt;strong&gt;Panel title&lt;/strong&gt; field in the right sidebar, you can enter a name for your panel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F00lh5b9bmgldnu3ojz11.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F00lh5b9bmgldnu3ojz11.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, click the &lt;strong&gt;Apply&lt;/strong&gt; button at the top right corner and your panel is added to the dashboard. Do not forget to save the dashboard by means of the &lt;strong&gt;Save dashboard&lt;/strong&gt; icon next to the &lt;strong&gt;Add panel&lt;/strong&gt; icon.&lt;/p&gt;

&lt;p&gt;Set some load to the application and see what happens to the metrics on the dashboard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;watch &lt;span class="nt"&gt;-n&lt;/span&gt; 5 curl http://localhost:8080/endPoint1
&lt;span class="nv"&gt;$ &lt;/span&gt;watch &lt;span class="nt"&gt;-n&lt;/span&gt; 10 curl http://localhost:8080/endPoint2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw715viuaxsk2mg6ngg8r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw715viuaxsk2mg6ngg8r.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post, you have learnt how you can set up some basic monitoring for a Spring Boot application. It is necessary to use a combination of Spring Actuator, Micrometer, Prometheus and Grafana, but these are quite easy to set up and configure. This is of course just a starting point, but from here on, it is possible to expand and configure more specific metrics for your application.&lt;/p&gt;

</description>
      <category>java</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting Started With RSocket Part 2</title>
      <dc:creator>mydeveloperplanet</dc:creator>
      <pubDate>Wed, 24 Feb 2021 18:37:42 +0000</pubDate>
      <link>https://dev.to/mydeveloperplanet/getting-started-with-rsocket-part-2-45ke</link>
      <guid>https://dev.to/mydeveloperplanet/getting-started-with-rsocket-part-2-45ke</guid>
      <description>&lt;p&gt;In this blog, you will continue where you left off after &lt;a href="https://mydeveloperplanet.com/2021/02/03/getting-started-with-rsocket-part-1/"&gt;Part 1&lt;/a&gt;. You will explore the RSocket communication models Fire-and-Forget, Request-Stream and Channel. For all of these models, you will create the server, client and a unit test.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://mydeveloperplanet.com/2021/02/03/getting-started-with-rsocket-part-1/"&gt;Part 1&lt;/a&gt;, you learnt the basics of the RSocket communication protocol. It is advised to read Part 1 first before continuing with Part 2. Remember that RSocket provides 4 communication models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request-Response (a stream of 1)&lt;/li&gt;
&lt;li&gt;Fire-and-Forget (no response)&lt;/li&gt;
&lt;li&gt;Request-Stream (a stream of many)&lt;/li&gt;
&lt;li&gt;Channel (bi-directional streams)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You covered Request-Response in Part 1, the others will be covered in Part 2.&lt;/p&gt;

&lt;p&gt;The source code being used in this post is of course available at &lt;a href="https://github.com/mydeveloperplanet/myrsocketplanet"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Fire-and-Forget Model
&lt;/h2&gt;

&lt;p&gt;The Fire-and-Forget model is quite similar to the Request-Response model. The only difference is that you do not expect a response to your request.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 The Server Side
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;RSocketServerController&lt;/code&gt; you create a method &lt;code&gt;fireAndForget&lt;/code&gt;. Because the request does not return anything, the return type of the method is &lt;code&gt;void&lt;/code&gt;. Again, with the annotation &lt;code&gt;@MessageMapping&lt;/code&gt; you define the name of the route. Just as with the Request-Response example, the server receives a &lt;code&gt;Notification&lt;/code&gt; message. In order to see something happening when the message is received, you just log the &lt;code&gt;Notification&lt;/code&gt; message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@MessageMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-fire-and-forget"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;fireAndForget&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received notification: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.2 The Client Side
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;RSocketClientController&lt;/code&gt; you create a method &lt;code&gt;fireAndForget&lt;/code&gt;. The implementation is identical to the Request-Response example except for the expected return type. Here you use &lt;code&gt;retrieveMono(Void.class)&lt;/code&gt; instead of &lt;code&gt;retrieveMono(Notification.class)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/fire-and-forget"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fireAndForget&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Test the Fire-And-Forget interaction model"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Send notification for my-fire-and-forget: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rSocketRequester&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-fire-and-forget"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retrieveMono&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Void&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start both the server and the client and invoke the URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;http://localhost:8080/fire-and-forget
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, no response is returned. In the logging of client and server, you can verify the sending and receiving messages.&lt;/p&gt;

&lt;p&gt;Client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Send notification &lt;span class="k"&gt;for &lt;/span&gt;my-fire-and-forget: Notification&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Client'&lt;/span&gt;, &lt;span class="nv"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Server'&lt;/span&gt;, &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Test the Fire-And-Forget interaction model'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Received notification: Notification&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Client'&lt;/span&gt;, &lt;span class="nv"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Server'&lt;/span&gt;, &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Test the Fire-And-Forget interaction model'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.3 The Test Side
&lt;/h3&gt;

&lt;p&gt;The test is again quite similar to the client code and the Request-Response example. In order to validate whether the &lt;code&gt;Mono&lt;/code&gt; does not emit any data, it is sufficient to call &lt;code&gt;verifyComplete&lt;/code&gt;. You do not need to call &lt;code&gt;consumeWithNext&lt;/code&gt;. If the &lt;code&gt;Mono&lt;/code&gt; does emit data, the test should fail. However, replacing the route &lt;code&gt;my-fire-and-forget&lt;/code&gt; into &lt;code&gt;my-request-response&lt;/code&gt; f.e., does not fail the test. It is unclear why this will not fail the test. If anyone has any suggestions or a solution, please add it into the comments of this blog.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testFireAndForget&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Send a fire-and-forget message&lt;/span&gt;
    &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rSocketRequester&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-fire-and-forget"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Test the Fire-And-Forget interaction model"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retrieveMono&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Void&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Assert that the result is a completed Mono.&lt;/span&gt;
    &lt;span class="nc"&gt;StepVerifier&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;verifyComplete&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Request-Stream Model
&lt;/h2&gt;

&lt;p&gt;With the Request-Stream model, you send a request to the server and you will receive a Stream of Notification messages.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 The Server Side
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;RSocketServerController&lt;/code&gt; you create a method &lt;code&gt;requestStream&lt;/code&gt;. This time, the server will return a &lt;code&gt;Flux&lt;/code&gt; of &lt;code&gt;Notification&lt;/code&gt; messages. Again, with the annotation &lt;code&gt;@MessageMapping&lt;/code&gt; you define the name of the route. In this example, upon receipt of a &lt;code&gt;Notification&lt;/code&gt; message, a &lt;code&gt;Flux&lt;/code&gt; is returned which emits a new &lt;code&gt;Notification&lt;/code&gt; every 3 seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@MessageMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-request-stream"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;requestStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received notification for my-request-stream: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Flux&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDestination&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSource&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"In response to: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.2 The Client Side
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;RSocketClientController&lt;/code&gt; you create a method &lt;code&gt;requestStream&lt;/code&gt;. The implementation is identical to the Request-Response example except for the expected return type. Here you use &lt;code&gt;retrieveFlux(Notification.class)&lt;/code&gt; instead of &lt;code&gt;retrieveMono(Notification.class)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/request-stream"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;requestStream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Test the Request-Stream interaction model"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Send notification for my-request-stream: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;notificationFlux&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rSocketRequester&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-request-stream"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retrieveFlux&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TEXT_EVENT_STREAM&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notificationFlux&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start both the server and the client and invoke the URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:8080/request-stream
data:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"source"&lt;/span&gt;:&lt;span class="s2"&gt;"Server"&lt;/span&gt;,&lt;span class="s2"&gt;"destination"&lt;/span&gt;:&lt;span class="s2"&gt;"Client"&lt;/span&gt;,&lt;span class="s2"&gt;"text"&lt;/span&gt;:&lt;span class="s2"&gt;"In response to: Test the Request-Stream interaction model"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

data:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"source"&lt;/span&gt;:&lt;span class="s2"&gt;"Server"&lt;/span&gt;,&lt;span class="s2"&gt;"destination"&lt;/span&gt;:&lt;span class="s2"&gt;"Client"&lt;/span&gt;,&lt;span class="s2"&gt;"text"&lt;/span&gt;:&lt;span class="s2"&gt;"In response to: Test the Request-Stream interaction model"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

data:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"source"&lt;/span&gt;:&lt;span class="s2"&gt;"Server"&lt;/span&gt;,&lt;span class="s2"&gt;"destination"&lt;/span&gt;:&lt;span class="s2"&gt;"Client"&lt;/span&gt;,&lt;span class="s2"&gt;"text"&lt;/span&gt;:&lt;span class="s2"&gt;"In response to: Test the Request-Stream interaction model"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

data:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"source"&lt;/span&gt;:&lt;span class="s2"&gt;"Server"&lt;/span&gt;,&lt;span class="s2"&gt;"destination"&lt;/span&gt;:&lt;span class="s2"&gt;"Client"&lt;/span&gt;,&lt;span class="s2"&gt;"text"&lt;/span&gt;:&lt;span class="s2"&gt;"In response to: Test the Request-Stream interaction model"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the server emits every 3 seconds a Notification. In the logging of client and server, you can verify the sending and receiving messages.&lt;/p&gt;

&lt;p&gt;Client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Send notification &lt;span class="k"&gt;for &lt;/span&gt;my-request-stream: Notification&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Client'&lt;/span&gt;, &lt;span class="nv"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Server'&lt;/span&gt;, &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Test the Request-Stream interaction model'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Received notification &lt;span class="k"&gt;for &lt;/span&gt;my-request-stream: Notification&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Client'&lt;/span&gt;, &lt;span class="nv"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Server'&lt;/span&gt;, &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Test the Request-Stream interaction model'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.3 The Test Side
&lt;/h3&gt;

&lt;p&gt;The test is again similar to the client code. During verification, you verify the first message received, then verify whether 5 messages are received and finally verify the last message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testRequestStream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Send a request message&lt;/span&gt;
    &lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rSocketRequester&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-request-stream"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Test the Request-Stream interaction model"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retrieveFlux&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Verify that the response messages contain the expected data&lt;/span&gt;
    &lt;span class="nc"&gt;StepVerifier&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;consumeNextWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
         &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSource&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
         &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDestination&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                                             &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
         &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"In response to: Test the Request-Stream interaction model"&lt;/span&gt;&lt;span class="o"&gt;);})&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;expectNextCount&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;consumeNextWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
         &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSource&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
         &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDestination&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                                             &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
         &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"In response to: Test the Request-Stream interaction model"&lt;/span&gt;&lt;span class="o"&gt;);})&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;thenCancel&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Channel Model
&lt;/h2&gt;

&lt;p&gt;The Channel model is a bit more complicated than the other models you have seen so far. Here you will send a &lt;code&gt;Flux&lt;/code&gt; and as a response a &lt;code&gt;Flux&lt;/code&gt; will be returned. This provides you the ability to send messages back and forth like within a chat conversation for example.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 The Server Side
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;RSocketServerController&lt;/code&gt; you create a method &lt;code&gt;channel&lt;/code&gt;. You will upon receipt of a &lt;code&gt;Notification&lt;/code&gt; increment a counter and every one second the result of the counter &lt;code&gt;notificationCount&lt;/code&gt; will be sent to the client. In order to be able to follow what is happening, you add logging at receiving the &lt;code&gt;Notification&lt;/code&gt; and when the result is returned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@MessageMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-channel"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;notifications&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;AtomicLong&lt;/span&gt; &lt;span class="n"&gt;notificationCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AtomicLong&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;notifications&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;doOnNext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received notification for channel: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;notificationCount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;incrementAndGet&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
         &lt;span class="o"&gt;})&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;switchMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; 
&lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;numberOfMessages&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AtomicLong&lt;/span&gt; &lt;span class="n"&gt;notificationCount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;notificationCount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Return flux with count: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
         &lt;span class="o"&gt;}.&lt;/span&gt;&lt;span class="na"&gt;numberOfMessages&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notificationCount&lt;/span&gt;&lt;span class="o"&gt;))).&lt;/span&gt;&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.2 The Client Side
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;RSocketClientController&lt;/code&gt; you create a method &lt;code&gt;channel&lt;/code&gt;. You need to create a &lt;code&gt;Flux&lt;/code&gt;. In order to accomplish this, you create 3 &lt;code&gt;Mono&lt;/code&gt; &lt;code&gt;Notification&lt;/code&gt; items, one with a delay of 0 seconds (&lt;code&gt;notification0&lt;/code&gt;), one with a delay of 2 seconds (&lt;code&gt;notification2&lt;/code&gt;) and one with a delay of 5 seconds (&lt;code&gt;notification5&lt;/code&gt;). You create the &lt;code&gt;Flux&lt;/code&gt; &lt;code&gt;notifications&lt;/code&gt; with a combination of the &lt;code&gt;Mono&lt;/code&gt;‘s you just created. Every time the &lt;code&gt;Flux&lt;/code&gt; emits a Notification, you log this in order to be able to follow what is happening. Finally, you send the &lt;code&gt;Flux&lt;/code&gt; to the RSocket channel and retrieve the response as a &lt;code&gt;Flux&lt;/code&gt; of &lt;code&gt;Long&lt;/code&gt; and return it to the caller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/channel"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;notification0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;just&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Test the Channel interaction model"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;notification2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;just&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Test the Channel interaction model"&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;delayElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;notification5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;just&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Test the Channel interaction model"&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;delayElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;notifications&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;concat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;doOnNext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Send notification for my-channel"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;numberOfNotifications&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rSocketRequester&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-channel"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notifications&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retrieveFlux&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TEXT_EVENT_STREAM&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numberOfNotifications&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start both the server and the client and invoke the URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:8080/channel
data:1
data:1
data:1
data:1
data:3
data:3
data:4
data:4
data:5
data:5
data:6
data:6
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is as expected. First the &lt;code&gt;notification0&lt;/code&gt; is sent, after 5 seconds (&lt;code&gt;notification5&lt;/code&gt;) the following &lt;code&gt;Notification&lt;/code&gt; is sent together with a notification0, 2 seconds later a &lt;code&gt;notification2&lt;/code&gt;, 2 seconds later a new one, and finally 2 seconds later the last one. After the last result, the &lt;code&gt;Flux&lt;/code&gt; will keep on transmitting a count of 6. In the logging of client and server, you can verify the sending and receiving messages. This time including the timestamps, the complete logging contains even more information which is left out for brevity purposes. You should take a look at it more closely when running the examples yourself. Important to notice are the &lt;code&gt;onNext&lt;/code&gt; log statements which occur each second and correspond with the response the server is sending.&lt;/p&gt;

&lt;p&gt;Client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;17:01:19.820 Send notification &lt;span class="k"&gt;for &lt;/span&gt;my-channel
17:01:24.849 Send notification &lt;span class="k"&gt;for &lt;/span&gt;my-channel
17:01:24.879 Send notification &lt;span class="k"&gt;for &lt;/span&gt;my-channel
17:01:26.881 Send notification &lt;span class="k"&gt;for &lt;/span&gt;my-channel
17:01:28.908 Send notification &lt;span class="k"&gt;for &lt;/span&gt;my-channel
17:01:30.935 Send notification &lt;span class="k"&gt;for &lt;/span&gt;my-channel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;17:01:19.945 Received notification &lt;span class="k"&gt;for &lt;/span&gt;channel: Notification&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Client'&lt;/span&gt;, &lt;span class="nv"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Server'&lt;/span&gt;, &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Test the Channel interaction model'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
17:01:19.947 Return flux with count: 1
17:01:20.949 onNext&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;
17:01:21.947 onNext&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;
17:01:22.947 onNext&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;
17:01:23.947 onNext&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;
17:01:24.881 Received notification &lt;span class="k"&gt;for &lt;/span&gt;channel: Notification&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Client'&lt;/span&gt;, &lt;span class="nv"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Server'&lt;/span&gt;, &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Test the Channel interaction model'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
17:01:24.882 Return flux with count: 2
17:01:24.884 Received notification &lt;span class="k"&gt;for &lt;/span&gt;channel: Notification&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Client'&lt;/span&gt;, &lt;span class="nv"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Server'&lt;/span&gt;, &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Test the Channel interaction model'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
17:01:24.885 Return flux with count: 3
17:01:25.886 onNext&lt;span class="o"&gt;(&lt;/span&gt;3&lt;span class="o"&gt;)&lt;/span&gt;
17:01:26.886 onNext&lt;span class="o"&gt;(&lt;/span&gt;3&lt;span class="o"&gt;)&lt;/span&gt;
17:01:26.909 Received notification &lt;span class="k"&gt;for &lt;/span&gt;channel: Notification&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Client'&lt;/span&gt;, &lt;span class="nv"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Server'&lt;/span&gt;, &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Test the Channel interaction model'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
17:01:26.909 Return flux with count: 4
17:01:27.910 onNext&lt;span class="o"&gt;(&lt;/span&gt;4&lt;span class="o"&gt;)&lt;/span&gt;
17:01:28.910 onNext&lt;span class="o"&gt;(&lt;/span&gt;4&lt;span class="o"&gt;)&lt;/span&gt;
17:01:28.936 Received notification &lt;span class="k"&gt;for &lt;/span&gt;channel: Notification&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Client'&lt;/span&gt;, &lt;span class="nv"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Server'&lt;/span&gt;, &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Test the Channel interaction model'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
17:01:28.937 Return flux with count: 5
17:01:29.937 onNext&lt;span class="o"&gt;(&lt;/span&gt;5&lt;span class="o"&gt;)&lt;/span&gt;
17:01:30.937 onNext&lt;span class="o"&gt;(&lt;/span&gt;5&lt;span class="o"&gt;)&lt;/span&gt;
17:01:30.963 Received notification &lt;span class="k"&gt;for &lt;/span&gt;channel: Notification&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Client'&lt;/span&gt;, &lt;span class="nv"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Server'&lt;/span&gt;, &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Test the Channel interaction model'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
17:01:30.964 Return flux with count: 6
17:01:31.964 onNext&lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;
17:01:32.964 onNext&lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.3 The Test Side
&lt;/h3&gt;

&lt;p&gt;The test is similar as the client code. You send the messages to the channel and verify the resulting counts. The repeating elements in the test are left out for brevity, the complete test is available at GitHub.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testChannel&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;notification0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;just&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Test the Channel interaction model"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;notification2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;just&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Test the Channel interaction model"&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;delayElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;notification5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;just&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Test the Channel interaction model"&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;delayElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;notifications&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;concat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Send a request message&lt;/span&gt;
    &lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rSocketRequester&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-channel"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notifications&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retrieveFlux&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Verify that the response messages contain the expected data&lt;/span&gt;
    &lt;span class="nc"&gt;StepVerifier&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;consumeNextWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;consumeNextWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="o"&gt;})&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;thenCancel&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Conclusion
&lt;/h2&gt;

&lt;p&gt;You have learnt how to create a server, client and a unit test for the RSocket communication models Fire-and-Forget, Request-Streams and Channel. By now, you will have the basic knowledge in order to do some exploring, experimentation yourself.&lt;/p&gt;

</description>
      <category>java</category>
      <category>rsocket</category>
      <category>springboot</category>
      <category>unittest</category>
    </item>
    <item>
      <title>Getting Started With RSocket Part 1</title>
      <dc:creator>mydeveloperplanet</dc:creator>
      <pubDate>Wed, 10 Feb 2021 18:30:35 +0000</pubDate>
      <link>https://dev.to/mydeveloperplanet/getting-started-with-rsocket-part-1-442d</link>
      <guid>https://dev.to/mydeveloperplanet/getting-started-with-rsocket-part-1-442d</guid>
      <description>&lt;p&gt;In this blog, you will learn the basics of RSocket, a binary application protocol which supports Reactive Streams. After the introduction, you will learn how to use RSocket in combination with Spring Boot. Enjoy!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;RSocket is a binary protocol to be used on top of TCP or WebSockets. RSocket is a communication protocol which embraces the &lt;a href="https://www.reactivemanifesto.org/"&gt;Reactive principles&lt;/a&gt;. This means that RSocket uses asynchronuous communication. It is also suited for push notifications. When using HTTP for example, there will be a need for polling in order to check whether new messages are available. This causes unnessary network load. RSocket provides a solution for this. There are 4 communication models which can be used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request-Response (a stream of 1)&lt;/li&gt;
&lt;li&gt;Fire-and-Forget (no response)&lt;/li&gt;
&lt;li&gt;Request-Stream (a stream of many)&lt;/li&gt;
&lt;li&gt;Channel (bi-directional streams)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RSocket is situated at the OSI layer 5/6 and therefore at the Application Layer of the TCP/IP Model.&lt;/p&gt;

&lt;p&gt;In the next sections, you will find examples for each communication model: the server side, client side and a unit test. The source code being used in this post is of course available at &lt;a href="https://github.com/mydeveloperplanet/myrsocketplanet"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Reference Documentation
&lt;/h2&gt;

&lt;p&gt;Before getting started, it is useful to know where some interesting documentation can be found. During writing this blog, it appeared that reference documentation, examples, etc. cannot easily be found. This list should give you a flying start when taking your first steps with RSocket.&lt;/p&gt;

&lt;p&gt;All information about the protocol, the specification, implementations can be found at the official &lt;a href="https://rsocket.io/"&gt;RSocket website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.spring.io/spring-framework/docs/5.3.2/reference/html/web-reactive.html#rsocket"&gt;Spring Framework’s support&lt;/a&gt; for the RSocket protocol.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-rsocket"&gt;Spring Boot reference section&lt;/a&gt; for the RSocket protocol.&lt;/p&gt;

&lt;p&gt;Ben Wilcock has written some awesome blogs about the RSocket protocol. The complete list can be found at &lt;a href="https://github.com/benwilcock/spring-rsocket-demo"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Request-Response Model
&lt;/h2&gt;

&lt;p&gt;The Request-Response model will allow you to send one request and receive one response in return. First thing to do, is to set up a basic Spring Boot application. Navigate to the &lt;a href="https://start.spring.io/"&gt;Spring Initializr website&lt;/a&gt;, add dependency &lt;strong&gt;RSocket&lt;/strong&gt; and create the project which you can open in your favorite IDE. When checking the &lt;code&gt;pom&lt;/code&gt;, you notice that the &lt;code&gt;spring-boot-starter-rsocket&lt;/code&gt; dependency and the dependency &lt;code&gt;reactor-test&lt;/code&gt; are added. The first one will enable RSocket support in your Spring Boot application, the second one is needed for testing purposes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-rsocket&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.projectreactor&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;reactor-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source code at GitHub is divided into two Maven modules, one for the &lt;code&gt;server&lt;/code&gt; and one for the &lt;code&gt;client&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In order to exchange information between client and server, you create a &lt;code&gt;Notification&lt;/code&gt; data class which will be the item to transport via RSocket. The &lt;code&gt;Notification&lt;/code&gt; class contains a &lt;code&gt;Source&lt;/code&gt;, a &lt;code&gt;Destination&lt;/code&gt; and some free &lt;code&gt;Text&lt;/code&gt;. The &lt;code&gt;toString&lt;/code&gt; implementation will be used for logging purposes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getSource&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getDestination&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getText&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Notification{"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="s"&gt;"source='"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sc"&gt;'\''&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="s"&gt;", destination='"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sc"&gt;'\''&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="s"&gt;", text='"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sc"&gt;'\''&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="sc"&gt;'}'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.1 The Server Side
&lt;/h3&gt;

&lt;p&gt;You create a &lt;code&gt;RsocketServerController&lt;/code&gt; and annotate it with &lt;code&gt;@Controller&lt;/code&gt;. In order to create your first RSocket Request-Response example, you just add a method &lt;code&gt;requestResponse&lt;/code&gt; which takes a &lt;code&gt;Notification&lt;/code&gt;, logs the received &lt;code&gt;Notification&lt;/code&gt; and returns a new &lt;code&gt;Notification&lt;/code&gt; where you swap the received source and destination and add a simple text to it. In order to make it a RSocket request, you need to annotate the method with &lt;code&gt;@MessageMapping&lt;/code&gt; and give it a name, e.g. &lt;code&gt;my-request-response&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Controller&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RsocketServerController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nc"&gt;Logger&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RsocketServerController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@MessageMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-request-response"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="nf"&gt;requestResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received notification for my-request-response: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDestination&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSource&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"In response to: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to ensure that the RSocket server is started, you also need to add the port to the &lt;code&gt;application.properties&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spring.rsocket.server.port=7000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mvn spring-boot:run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the logging you notice that the Netty Webserver has started. Netty is the reactive counterpart of a Jetty Webserver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Netty RSocket started on port&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;: 7000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.2 The Client Side
&lt;/h3&gt;

&lt;p&gt;The client side is a little bit more complex. You will again create a Spring Boot application which will send a &lt;code&gt;Notification&lt;/code&gt; message to the server. Sending the message will be invoked by means of an http call. Therefore, you add the dependency &lt;code&gt;spring-boot-starter-webflux&lt;/code&gt; to the client &lt;code&gt;pom&lt;/code&gt;. Beware that you cannot use &lt;code&gt;spring-boot-starter-web&lt;/code&gt;, you need to use the reactive webflux variant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-webflux&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure that you do not define the port in the &lt;code&gt;application.properties&lt;/code&gt;, otherwise a RSocket server will be started and that is not what is needed for your client. When you do so, the following error will appear in your console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;2021-01-02 12:04:58.853 ERROR 19058 &lt;span class="nt"&gt;---&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;           main] o.s.boot.SpringApplication               : Application run failed
org.springframework.context.ApplicationContextException: Failed to start bean &lt;span class="s1"&gt;'rSocketServerBootstrap'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; nested exception is reactor.netty.ChannelBindException: Failed to &lt;span class="nb"&gt;bind &lt;/span&gt;on &lt;span class="o"&gt;[&lt;/span&gt;0.0.0.0:7000]
...
Caused by: reactor.netty.ChannelBindException: Failed to &lt;span class="nb"&gt;bind &lt;/span&gt;on &lt;span class="o"&gt;[&lt;/span&gt;0.0.0.0:7000]
    Suppressed: java.lang.Exception: &lt;span class="c"&gt;#block terminated with an error&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You create a &lt;code&gt;RsocketClientController&lt;/code&gt; and annotate it with &lt;code&gt;@RestController&lt;/code&gt;. Next, you need to create a &lt;code&gt;RSocketRequester&lt;/code&gt; instance in order to be able to connect to the RSocket server. In the &lt;code&gt;requestResponse&lt;/code&gt; method, you create the &lt;code&gt;Notification&lt;/code&gt; message (for ease of use, just copy the &lt;code&gt;Notification&lt;/code&gt; class from the server module and make sure that it is also present on the client side) and with the &lt;code&gt;rSocketRequester&lt;/code&gt; instance, you specify the route you want the message to be sent to (name equals the name as specified with the &lt;code&gt;@MessageMapping&lt;/code&gt; annotation on the server side), the data you want to send and finally the response you expect. The response will be a &lt;code&gt;Mono&lt;/code&gt; which means that you expect one response from the server and the response needs to be a &lt;code&gt;Notification&lt;/code&gt; message. The message itself is returned to the caller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RsocketClientController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;CLIENT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Client"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;SERVER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Server"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;RSocketRequester&lt;/span&gt; &lt;span class="n"&gt;rSocketRequester&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nc"&gt;Logger&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RsocketClientController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;RsocketClientController&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Autowired&lt;/span&gt; &lt;span class="nc"&gt;RSocketRequester&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rSocketRequester&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tcp&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"localhost"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/request-response"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;requestResponse&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Test the Request-Response interaction model"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Send notification for my-request-response: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rSocketRequester&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-request-response"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retrieveMono&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start both the server and the client and invoke the URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:8080/request-response
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"source"&lt;/span&gt;:&lt;span class="s2"&gt;"Server"&lt;/span&gt;,&lt;span class="s2"&gt;"destination"&lt;/span&gt;:&lt;span class="s2"&gt;"Client"&lt;/span&gt;,&lt;span class="s2"&gt;"text"&lt;/span&gt;:&lt;span class="s2"&gt;"In response to: Test the Request-Response interaction model"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the server response is returned. In the logging of client and server, you can verify the sending and receiving messages.&lt;/p&gt;

&lt;p&gt;Client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Send notification &lt;span class="k"&gt;for &lt;/span&gt;my-request-response: Notification&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Client'&lt;/span&gt;, &lt;span class="nv"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Server'&lt;/span&gt;, &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Test the Request-Response interaction model'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Received notification &lt;span class="k"&gt;for &lt;/span&gt;my-request-response: Notification&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Client'&lt;/span&gt;, &lt;span class="nv"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Server'&lt;/span&gt;, &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Test the Request-Response interaction model'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.3 The Test Side
&lt;/h3&gt;

&lt;p&gt;Creating a test for the server code is quite similar as creating the client code. A &lt;code&gt;RSocketRequester&lt;/code&gt; needs to be created in order to setup the connection. Sending a message is identical to the client code, only this time you put the response into a &lt;code&gt;result&lt;/code&gt; variable of type &lt;code&gt;Mono&amp;lt;Notification&amp;gt;&lt;/code&gt;. You can use this &lt;code&gt;result&lt;/code&gt; variable in a &lt;code&gt;StepVerifier&lt;/code&gt; in order to validate the response which is received. With a &lt;code&gt;StepVerifier&lt;/code&gt; you can verify reactive responses in your unit test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyRsocketServerPlanetApplicationTests&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;CLIENT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Client"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;SERVER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Server"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;RSocketRequester&lt;/span&gt; &lt;span class="n"&gt;rSocketRequester&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@BeforeAll&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setupOnce&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Autowired&lt;/span&gt; &lt;span class="nc"&gt;RSocketRequester&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${spring.rsocket.server.port}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;rSocketRequester&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tcp&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"localhost"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testRequestResponse&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Send a request message&lt;/span&gt;
        &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rSocketRequester&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-request-response"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Test the Request-Response interaction model"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retrieveMono&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Verify that the response message contains the expected data&lt;/span&gt;
        &lt;span class="nc"&gt;StepVerifier&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;consumeNextWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSource&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                    &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDestination&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                    &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"In response to: Test the Request-Response interaction model"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="o"&gt;})&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;verifyComplete&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Conclusion
&lt;/h2&gt;

&lt;p&gt;You have learnt the basics of the RSocket application protocol and explored how to create a server, client and unit test for the Request-Response communication model. In the &lt;a href="https://mydeveloperplanet.com/2021/02/17/getting-started-with-rsocket-part-2/"&gt;next&lt;/a&gt; blog, you will learn how to create a server, client and unit test for the remaining three communication models.&lt;/p&gt;

</description>
      <category>java</category>
      <category>rsocket</category>
      <category>springboot</category>
      <category>unittest</category>
    </item>
  </channel>
</rss>
