Programming

basic programming

Error user_session_not_found when Using Keycloak’s UserInfo API

Had this error on Keycloak console,

09:49:35,417 WARN  [org.keycloak.events] (default task-145) type=USER_INFO_REQUEST_ERROR, 
realmId=internal, clientId=my-client-id, userId=null, ipAddress=10.20.24.35, 
error=user_session_not_found, auth_method=validate_access_token

Basically it happens when a specific user hitting a UserInfo API request bringing their active JWT token. JWT token is generated after a user successfully login to Keycloak, either via Login page or Rest API, and to be used in their internal application.

Also the error seems happening, generated JWT token seems to be invalid after 30minutes despite we update access token lifespan into 1 hour.

Finally i realized that this error keeps happening because i was updating the wrong configuration. It is supposed to be the “SSO Session Idle” configuration, in the “Tokens” tab in “Realm Settings” that need to be updated.

After change it into 1 Hour, i can see that my JWT token is successfully validated using UserInfo API for at most 1 hour after being created.

Dockerfile for Deploying Applications in JBoss EAP 7.4, and on top of Openshift 4

Openshift have a different permission right because by default, any containers deployed in Openshift will gets a random user ID. Therefore it needs a specific approach when creating a containerized apps, especially in regards to folder access rights. A simple chmod or chown commands wont be sufficient enough for this purpose.

Long story short, we can use below Dockerfile to be use to deploy an existing war file into JBoss EAP base image and push the result into Openshift 4.

FROM registry.redhat.io/jboss-eap-7/eap74-openjdk11-openshift-rhel8

ENV DISABLE_EMBEDDED_JMS_BROKER=true

COPY target/*.war $JBOSS_HOME/standalone/deployments/

USER root
RUN chgrp -R 0 $JBOSS_HOME/standalone/deployments/ && \
	chmod -R g=u $JBOSS_HOME/standalone/deployments/
USER 185

EXPOSE 8080

Run below command to build the image, can use either Podman or Docker command for it.

$ podman build -t custom-app-name .

Hello World App Using Spring Boot and Apache Camel

Based on wikipedia, Apache Camel is an open source framework for message-oriented middleware with a rule-based routing and mediation engine that provides a Java object-based implementation of the Enterprise Integration Patterns.

The good thing about Apache Camel is that it also has a great flexibility where we can deploy Apache Camel on top of Spring Boot, providing an agile microservice EIP and SOA approach.

So lets start by defining what kind of libraries needed for this project,

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.edw</groupId>
    <artifactId>camel-hello-world</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <camel.version>3.17.0</camel.version>
        <spring-boot.version>2.7.1</spring-boot.version>
        <start-class>com.edw.Applicationn</start-class>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.camel.springboot</groupId>
                <artifactId>camel-spring-boot-dependencies</artifactId>
                <version>${camel.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!--        spring boot     -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--        camel   -->
        <dependency>
            <groupId>org.apache.camel.springboot</groupId>
            <artifactId>camel-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-test-spring-junit5</artifactId>
            <scope>test</scope>
        </dependency>

        <!--        unit test   -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>json-path</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>${start-class}</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>3.0.0-M5</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>

                <configuration>
                    <classpathDependencyExcludes>
                        <classpathDependencyExcludes>${project.groupId}:${project.artifactId}
                        </classpathDependencyExcludes>
                    </classpathDependencyExcludes>
                    <additionalClasspathElements>
                        <additionalClasspathElement>${project.build.outputDirectory}</additionalClasspathElement>
                    </additionalClasspathElements>
                    <reportsDirectory>${project.build.directory}/surefire-reports</reportsDirectory>
                </configuration>
            </plugin>

        </plugins>
    </build>

</project>

Create a java main class,

package com.edw;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

A Spring controller, where we call Camel’s route

package com.edw.controller;

import org.apache.camel.ProducerTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;

@RestController
@RequestMapping("/api")
public class ApiController {

    @Autowired
    private ProducerTemplate template;

    @GetMapping("/v1/hello")
    public HashMap getHello(@RequestParam("name") String name) {
        return (HashMap) template.requestBody("direct:getHelloWorld", name);
    }
}

And a Camel Route to do all the Enterprise Integration Pattern, where it do a json response

package com.edw.route;

import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
import java.util.HashMap;

@Component
public class ApiRoute extends RouteBuilder {

    @Override
    public void configure() {
        // say helloworld
        from("direct:getHelloWorld")
                .routeId("getHelloWorld")
                .tracing()
                .log("calling getHelloWorld")
                .process(exchange -> {
                    String name = (String) exchange.getIn().getBody();
                    exchange.getMessage().setBody(new HashMap<>(){{
                        put("hello", name);
                    }});
                })
                .end();
    }
}

Code for this post can be found on below github repo,

https://github.com/edwin/spring-boot-camel-hello-world

Have fun with Spring Boot and Camel 🙂

Dockerfile for Creating a Containerized JBoss EAP 7.4

This is a simple Dockerfile for creating a containerized application on top of JBoss EAP

FROM registry.redhat.io/jboss-eap-7/eap74-openjdk11-openshift-rhel8

COPY target/*.war $JBOSS_HOME/standalone/deployments/

USER root
RUN chown jboss:jboss -R $JBOSS_HOME/standalone/deployments/
USER jboss

EXPOSE 8080

And make sure our .war files is located in target folder.

Canary Deployment with Openshift 4 Route

Doing a Canary deployment using Openshift is pretty much straight forward, where we can leverage Openshift’s Route feature to do routing to multiple different application versions.

Before we go far, we need to understand the difference between a Blue Green deployment, and Canary deployment. Basically in Blue Green deployment, we are having the same application but with 2 different version (blue and green version) and we can route all the request to either blue, or green version. So at Blue Green deployment, all the requests would go either to blue, or to green version.

But on Canary, it’s like Blue Green deployment but we can change the traffic percentage partially. For example 90percent of the traffic would go to blue version while 10percent go to green one.

For this demo, lets create two different apps. Both apps are using PHP, with a different content for simulating blue-green deployment.

First create our first PHP app,

$ mkdir php-hello-world-1

and create index.php file inside it

<?php
echo "<h2>We are at version one</h2>";
echo "Hello world!<br>";

Build it and deploy into Openshift

$ oc new-build --name=php-app-01 --image-stream=php:7.4-ubi8 --binary=true
$ oc start-build php-app-01  --from-dir=.
$ oc new-app php-app-01 --name=php-app-01

Next is creating our second PHP app,

$ mkdir php-hello-world-2

With index.php inside it,

<?php
echo "<h2>We are at version TWO</h2>";
echo "Hello world!<br>";

Build and deploy our second PHP app into Openshift

$ oc new-build --name=php-app-02 --image-stream=php:7.4-ubi8 --binary=true
$ oc start-build php-app-02  --from-dir=.
$ oc new-app php-app-02 --name=php-app-02

We can see the result on our Openshift dashboard

Now lets create a route,

$ oc create route edge --service php-app-01 php-app

And split the route traffic, 80 percent goes to app version 1, and 20 percent goes to app version 2.

$ oc set route-backends php-app php-app-01=80 php-app-02=20

And check the result on Openshift dashboard,

Now, lets do curl testing to see whether request to that corresponding route are being distibuted into 2 different application version,

$ for i in `seq 1 20`; do curl -k https://php-app-route-url ;echo ; done
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version TWO</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version TWO</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version TWO</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version TWO</h2>Hello world!<br>

And we can see that 4 out of 20 request are being served from the second PHP application. For more testing, lets change the distribution weight

oc set route-backends php-app php-app-01=50 php-app-02=50

And do more testing,

$ for i in `seq 1 20`; do curl -k https://php-app-route-url ;echo ; done
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version TWO</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version TWO</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version TWO</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version TWO</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version TWO</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version TWO</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version TWO</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version TWO</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version TWO</h2>Hello world!<br>
<h2>We are at version one</h2>Hello world!<br>
<h2>We are at version TWO</h2>Hello world!<br>

And there we can see that requests are being split, half goes to version 1 and the rest goes to version 2.