<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Development &#8211; Manuel Bogner&#039;s Blog</title>
	<atom:link href="https://blog.mbo.dev/archives/category/dev/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.mbo.dev</link>
	<description>Solutions to everyday IT problems</description>
	<lastBuildDate>Fri, 11 Apr 2025 12:05:06 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://blog.mbo.dev/wp-content/uploads/2022/11/cropped-cropped-mbo-white_opt-32x32.png</url>
	<title>Development &#8211; Manuel Bogner&#039;s Blog</title>
	<link>https://blog.mbo.dev</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Install Python 3.13 without GIL on Apple Silicone</title>
		<link>https://blog.mbo.dev/archives/2055</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Fri, 11 Apr 2025 11:27:36 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<guid isPermaLink="false">https://blog.mbo.dev/?p=2055</guid>

					<description><![CDATA[The following script shows you steps to on an Apple Silicone Mac. I&#8217;ve also tested it with a script that allows to run with different numbers of parallel threads: Run on locally built python with GIL disabled: Run with same python version from brew with GIL enabled: Disabling GIL brings runtime down to 2.47 seconds [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>The following script shows you steps to</p>



<ul class="wp-block-list">
<li>download the python source code</li>



<li>compile it</li>



<li>integrate it into pyenv</li>



<li>and then use it</li>
</ul>



<p>on an Apple Silicone Mac.</p>



<pre class="wp-block-code"><code># prepare dependencies
brew install openssl readline zlib xz

# checkout python
git clone https://github.com/python/cpython.git
mkdir $HOME/.pythons
cd cpython
git checkout v3.13.3

# build
./configure \
  --prefix=$HOME/.pythons/python-3.13.3-nogil \
  --disable-gil \
  --enable-optimizations \
  CPPFLAGS="-I$(brew --prefix)/opt/openssl/include" \
  LDFLAGS="-L$(brew --prefix)/opt/openssl/lib"
make -j$(sysctl -n hw.ncpu)
make install

# integrate into pyenv
mkdir -p ~/.pyenv/versions/3.13.3-nogil
ln -s $HOME/.pythons/python-3.13.3-nogil ~/.pyenv/versions/3.13.3-nogil
# verify installation
pyenv versions | grep 3.13.3

pyenv shell 3.13.3-nogil
python3 --version
```
</code></pre>



<p>I&#8217;ve also tested it with a script that allows to run with different numbers of parallel threads:</p>



<pre class="wp-block-code"><code>import sys
import sysconfig
import math
import time
import threading
import argparse


def compute_factorial(n: int) -&gt; None:
    """Compute the factorial of a single number (no return, just for load)."""
    math.factorial(n)


def multi_threaded_compute(numbers: list&#91;int], thread_count: int) -&gt; None:
    """Distribute factorial computations across multiple threads."""
    threads = &#91;]
    # Split the workload into chunks for each thread
    chunks = &#91;numbers&#91;i::thread_count] for i in range(thread_count)]

    def worker(sublist: list&#91;int]) -&gt; None:
        for num in sublist:
            compute_factorial(num)

    # Start threads
    for sublist in chunks:
        thread = threading.Thread(target=worker, args=(sublist,))
        threads.append(thread)
        thread.start()

    # Wait for all threads to finish
    for thread in threads:
        thread.join()

    print("All factorials computed.")


def check_gil_status() -&gt; None:
    """Print whether the GIL is active or disabled."""
    gil_status = sysconfig.get_config_var("Py_GIL_DISABLED")
    if gil_status is None:
        print("GIL status: Unknown or not supported on this build")
    elif gil_status == 0:
        print("GIL is active")
    elif gil_status == 1:
        print("GIL is disabled")


def parse_args() -&gt; int:
    """Parse and return the number of threads from command-line args."""
    parser = argparse.ArgumentParser(description="Multithreaded factorial computation")
    parser.add_argument(
        "threads",
        type=int,
        help="Number of threads to use (must be a positive integer)",
    )
    args = parser.parse_args()

    if args.threads &lt;= 0:
        parser.error("Thread count must be a positive integer")

    return args.threads


def main():
    print(f"Python version: {sys.version}")
    check_gil_status()

    thread_count = parse_args()
    numbers = &#91;100_000, 200_000, 300_000, 400_000, 500_000]

    start_time = time.time()
    multi_threaded_compute(numbers, thread_count)
    elapsed_time = time.time() - start_time

    print(f"Time taken: {elapsed_time:.2f} seconds")


if __name__ == "__main__":
    main()</code></pre>



<p>Run on locally built python with GIL disabled:</p>



<pre class="wp-block-code"><code>% time python3 test.py $(sysctl -n hw.ncpu)

Python version: 3.13.3 experimental free-threading build (tags/v3.13.3:6280bb54784, Apr 11 2025, 13:01:07) &#91;Clang 17.0.0 (clang-1700.0.13.3)]
GIL is disabled
All factorials computed.
Time taken: 2.47 seconds
python3 test.py $(sysctl -n hw.ncpu)  6.09s user 0.04s system 235% cpu 2.608 total</code></pre>



<p>Run with same python version from brew with GIL enabled:</p>



<pre class="wp-block-code"><code>% time python3 test.py $(sysctl -n hw.ncpu)

Python version: 3.13.3 (main, Apr  8 2025, 13:54:08) &#91;Clang 16.0.0 (clang-1600.0.26.6)]
GIL is active
All factorials computed.
Time taken: 6.26 seconds
python3 test.py $(sysctl -n hw.ncpu)  6.26s user 0.04s system 99% cpu 6.360 total</code></pre>



<p>Disabling GIL brings runtime down to 2.47 seconds instead of 6.26. Disabling the GIL made the program about 60% faster <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f680.png" alt="🚀" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Check the cpu usage! It went up to 235% instead of 99%.</p>



<p>Here a small comparison to run the same in Kotlin with focus on multithreading:</p>



<pre class="wp-block-code"><code>import java.math.BigInteger
import kotlin.system.exitProcess
import kotlin.system.measureTimeMillis
import java.util.concurrent.Executors

fun computeFactorial(n: Int): BigInteger {
    var result = BigInteger.ONE
    for (i in 2..n) {
        result = result.multiply(BigInteger.valueOf(i.toLong()))
    }
    return result
}

fun multiThreadedCompute(numbers: List&lt;Int>, threadCount: Int) {
    val executor = Executors.newFixedThreadPool(threadCount)

    val tasks = numbers.map { n ->
        Runnable { computeFactorial(n) }
    }

    tasks.forEach { executor.submit(it) }

    executor.shutdown()
    while (!executor.isTerminated) {
        Thread.sleep(50)
    }

    println("All factorials computed.")
}

fun main(args: Array&lt;String>) {
    if (args.isEmpty()) {
        println("Usage: kotlin FactorialBenchmark.kt &lt;thread_count>")
        exitProcess(1)
    }

    val threadCount = args&#91;0].toIntOrNull()
    if (threadCount == null || threadCount &lt;= 0) {
        println("Please provide a valid positive integer for thread count.")
        exitProcess(1)
    }

    println("Kotlin version: ${KotlinVersion.CURRENT}")
    println("Running with $threadCount threads")

    val numbers = listOf(100_000, 200_000, 300_000, 400_000, 500_000)

    val elapsed = measureTimeMillis {
        multiThreadedCompute(numbers, threadCount)
    }

    println("Time taken: ${"%.2f".format(elapsed / 1000.0)} seconds")
}</code></pre>



<p>Result for the kotlin program running within JVM:</p>



<pre class="wp-block-code"><code>% time java -jar benchmark.jar $(sysctl -n hw.ncpu)
Kotlin version: 2.1.20
Running with 10 threads
All factorials computed.
Time taken: 92,55 seconds
java -jar benchmark.jar $(sysctl -n hw.ncpu)  199.58s user 1.49s system 217% cpu 1:32.65 total</code></pre>



<p>So python is killing Kotlin in this use case. With or without GIL.</p>



<p>For completeness I also tried it with Java 21. It was nearly the same timing. So in this case it didn&#8217;t matter if the code was Kotlin or Java.</p>



<p>Interestingly even the go variant didn&#8217;t outperform python here:</p>



<pre class="wp-block-code"><code>% time go run main.go $(sysctl -n hw.ncpu)
Go version: go1.24.2
GIL is not applicable in Go (true concurrency with goroutines and OS threads)
All factorials computed.
Time taken: 16.26 seconds
go run main.go $(sysctl -n hw.ncpu)  32.77s user 3.37s system 217% cpu 16.626 total</code></pre>



<pre class="wp-block-code"><code>package main

import (
	"fmt"
	"math/big"
	"os"
	"runtime"
	"strconv"
	"time"
)

func computeFactorial(n int) *big.Int {
	result := big.NewInt(1)
	tmp := big.NewInt(1)
	for i := 2; i &lt;= n; i++ {
		tmp.SetInt64(int64(i))
		result.Mul(result, tmp)
	}
	return result
}

func worker(jobs &lt;-chan int, done chan&lt;- bool) {
	for num := range jobs {
		computeFactorial(num)
	}
	done &lt;- true
}

func multiThreadedCompute(numbers &#91;]int, threadCount int) {
	jobs := make(chan int, len(numbers))
	done := make(chan bool)

	// Start fixed number of workers
	for i := 0; i &lt; threadCount; i++ {
		go worker(jobs, done)
	}

	// Send jobs
	for _, num := range numbers {
		jobs &lt;- num
	}
	close(jobs)

	// Wait for all workers to finish
	for i := 0; i &lt; threadCount; i++ {
		&lt;-done
	}

	fmt.Println("All factorials computed.")
}

func checkGILStatus() {
	fmt.Println("GIL is not applicable in Go (true concurrency with goroutines and OS threads)")
}

func main() {
	fmt.Println("Go version:", runtime.Version())
	checkGILStatus()

	if len(os.Args) != 2 {
		fmt.Println("Usage: go run main.go &lt;thread_count>")
		os.Exit(1)
	}

	threadCount, err := strconv.Atoi(os.Args&#91;1])
	if err != nil || threadCount &lt;= 0 {
		fmt.Println("Invalid thread count: must be a positive integer")
		os.Exit(1)
	}

	numbers := &#91;]int{100_000, 200_000, 300_000, 400_000, 500_000}

	start := time.Now()
	multiThreadedCompute(numbers, threadCount)
	elapsed := time.Since(start)

	fmt.Printf("Time taken: %.2f seconds\n", elapsed.Seconds())
}</code></pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>CORS configuration for REST api accessed from browser</title>
		<link>https://blog.mbo.dev/archives/1988</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Wed, 15 Nov 2023 15:01:19 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Kotlin]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1988</guid>

					<description><![CDATA[To allow requests against a Spring Boot REST API from a website it is necessary to configure CORS properly. In case you don&#8217;t know your client URLs you can allow all clients by adding a mapping like the following in your Kotlin project: This allows access to all endpoints from all origins via GET, POST [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>To allow requests against a Spring Boot REST API from a website it is necessary to configure CORS properly. In case you don&#8217;t know your client URLs you can allow all clients by adding a mapping like the following in your Kotlin project:</p>



<pre class="wp-block-code"><code>@Configuration
class RestCorsConfig : WebMvcConfigurer {

    override fun addCorsMappings(@NonNull registry: CorsRegistry) {
        registry.addMapping("/**")
            .allowedMethods(HttpMethod.GET.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())
            .allowedOrigins("*")
    }
}</code></pre>



<p>This allows access to all endpoints from all origins via GET, POST and DELETE methods.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Start PostgreSQL correctly with docker-compose.yml</title>
		<link>https://blog.mbo.dev/archives/1967</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Thu, 24 Aug 2023 20:56:44 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Docker]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1967</guid>

					<description><![CDATA[I&#8217;m using PostgreSQL in a lot of projects and in my dev environments I always run it with docker-compose. But configuring it properly isn&#8217;t so clear if you want to use proper time zone and a health check. Attached the config with a custom database name &#8220;db&#8221; and an admin user. With this pg_isready is [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>I&#8217;m using PostgreSQL in a lot of projects and in my dev environments I always run it with docker-compose. But configuring it properly isn&#8217;t so clear if you want to use proper time zone and a health check. Attached the config with a custom database name &#8220;db&#8221; and an admin user. With this <code>pg_isready</code> is used in the healthcheck section with proper parameters based on the config. Works perfect for me like this. If you have improvements, please let me know.</p>



<pre class="wp-block-code"><code>version: "3.9"
services:

  postgres:
    image: postgres:15-alpine
    container_name: postgres
    hostname: postgres
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - postgres:/var/lib/postgresql/data/pgdata:rw
    ports:
      - "127.0.0.1:5432:5432"
    environment:
      POSTGRES_DB: db
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: admin
      PGDATA: /var/lib/postgresql/data/pgdata
      TZ: UTC
      PGTZ: UTC
    healthcheck:
      test: &#91; "CMD-SHELL", "pg_isready -U admin -d db" ]
      interval: 1s
      timeout: 5s
      retries: 10

volumes:
  postgres:</code></pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Intellij stuck with indexing</title>
		<link>https://blog.mbo.dev/archives/1957</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Wed, 02 Aug 2023 17:28:16 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1957</guid>

					<description><![CDATA[My intellij 2023.2 continued to repeatedly index something and it was obvious that it was the same again and again. The typical suggestions to invalidate caches plus restart didn&#8217;t work. Then I found the suggestion to edit the custom properties file via Help > Edit Custom Properties and add the following: Taken from https://www.jetbrains.com/help/objc/configuring-file-size-limit.html this [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>My intellij 2023.2 continued to repeatedly index something and it was obvious that it was the same again and again. The typical suggestions to invalidate caches plus restart didn&#8217;t work. Then I found the suggestion to edit the custom properties file via <em>Help > Edit Custom Properties</em> and add the following:</p>



<pre class="wp-block-code"><code>idea.max.intellisense.filesize=50</code></pre>



<p>Taken from <a rel="noreferrer noopener" href="https://www.jetbrains.com/help/objc/configuring-file-size-limit.html" target="_blank">https://www.jetbrains.com/help/objc/configuring-file-size-limit.html</a> this does the following:</p>



<pre class="wp-block-code"><code>The maximum size for files where AppCode enables coding assistance and design-time code inspection, is controlled by the idea.max.intellisense.filesize property. The default value is 2500 kilobytes.</code></pre>



<p>After setting this to 50 the indexing issue was gone. Setting it back to 2500 or even higher made the issue also reappear. Having that indexing starting over and over again was really annoying and also making code completion suck because displayed suggestions disappeared faster than I was able to select them.</p>



<p>Going to revisit this and give an update as soo as I have a better solution or the issue was fixed.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Redis Testcontainer with Kotlin and Spring Boot</title>
		<link>https://blog.mbo.dev/archives/1947</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Mon, 24 Jul 2023 21:26:56 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Kotlin]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1947</guid>

					<description><![CDATA[I found lots of samples for creating a redis testcontainer in Spring Boot tests. But none of them really worked. Below is a working base class with a sample. It is based on Spring Boot 3.1.2 and latest testcontainer version at time of writing (&#8220;org.testcontainers:testcontainers:1.18.3&#8221;). The testcontainer-redis versions were avoided on purpose because they were [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>I found lots of samples for creating a redis testcontainer in Spring Boot tests. But none of them really worked. Below is a working base class with a sample. It is based on Spring Boot 3.1.2 and latest testcontainer version at time of writing (&#8220;org.testcontainers:testcontainers:1.18.3&#8221;). The testcontainer-redis versions were avoided on purpose because they were only available with quite old testcontainer version.</p>



<pre class="wp-block-code"><code>import org.junit.jupiter.api.BeforeAll
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.testcontainers.containers.GenericContainer
import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
abstract class RedisTest {
    companion object {
        private const val REDIS_IMAGE = "redis:7-alpine"
        private const val REDIS_PORT = 6379

        private const val REDIS_CONFIG_HOST = "spring.data.redis.host"
        private const val REDIS_CONFIG_PORT = "spring.data.redis.port"

        private val redisContainer = GenericContainer&lt;Nothing>(REDIS_IMAGE).apply {
            withExposedPorts(REDIS_PORT)
            withReuse(true)
        }

        @JvmStatic
        @BeforeAll
        fun beforeAll() {
            if (!redisContainer.isRunning) {
                redisContainer.start()
                redisContainer.setWaitStrategy(HostPortWaitStrategy())
            }
        }

        @JvmStatic
        @DynamicPropertySource
        fun properties(registry: DynamicPropertyRegistry) {
            registry.add(
                REDIS_CONFIG_HOST,
                redisContainer::getContainerIpAddress
            )
            registry.add(
                REDIS_CONFIG_PORT,
                redisContainer::getFirstMappedPort
            )
        }
    }
}</code></pre>



<p>With this in place you can create a simple test by extending <code>RedisTest</code></p>



<pre class="wp-block-code"><code>import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.CsvSource
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.redis.core.RedisTemplate

class EventSourceTest @Autowired constructor(
    val redisTemplate: RedisTemplate&lt;String, String>
) : RedisTest() {

    @ParameterizedTest
    @CsvSource(
        value = &#91;
            "test1, 1",
            "test2, 2",
            "test3, 3",
        ]
    )
    fun redis(
        key: String,
        value: String
    ) {
        redisTemplate.opsForValue().set(
            key,
            value
        )
        assertThat(redisTemplate.opsForValue().getAndDelete(key)).isEqualTo(value)
    }

}</code></pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Using Generic Spring Boot Events</title>
		<link>https://blog.mbo.dev/archives/1911</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Wed, 08 Feb 2023 18:40:31 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Kotlin]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1911</guid>

					<description><![CDATA[Using Spring Events can be very handy to decouple code. But have you ever tried creating generic events and then write a listener for them? Java or Kotlin themselves don&#8217;t prevent you from doing so but when you try receiving the events you will see that it doesn&#8217;t work. The reason for this is type [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Using Spring Events can be very handy to decouple code. But have you ever tried creating generic events and then write a listener for them? Java or Kotlin themselves don&#8217;t prevent you from doing so but when you try receiving the events you will see that it doesn&#8217;t work. The reason for this is type erasure. Generics are being removed during compilation. </p>



<p>Here a simple code sample for generic Crud events in a JPA application:</p>



<pre class="wp-block-code"><code>open class CrudEvent&lt;ID : Serializable, T : AbstractEntity&lt;ID&gt;&gt;

@EventListener
fun onEvent(event: CrudEvent&lt;UUID, User&gt;) {}</code></pre>



<p>Looks good, right? But doesn&#8217;t work. You could replace <code>CrudEvent&lt;UUID, User></code> with <code>CrudEvent&lt;*, *></code> and it would start receiving events. But of course all and not just for User. Here a simple way to get around this problem:</p>



<pre class="wp-block-code"><code>class UserEvent: CrudEvent&lt;UUID, User&gt;

@EventListener
fun onEvent(event: UserEvent) {}</code></pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>OAuth2 client with Spring Boot 3</title>
		<link>https://blog.mbo.dev/archives/1907</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Tue, 31 Jan 2023 07:27:33 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Kotlin]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1907</guid>

					<description><![CDATA[With Spring Boot 3 some classes were deprecated and also the Keycloak adapter and the admin client stopped working in an application I am maintaining. In https://blog.coffeebeans.at/archives/1904 I already showed how to instantiate a WebClient which is the new way Spring wants us to call external APIs &#8211; not considering external libs like feign now. [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>With Spring Boot 3 some classes were deprecated and also the Keycloak adapter and the admin client stopped working in an application I am maintaining. In https://blog.coffeebeans.at/archives/1904 I already showed how to instantiate a WebClient which is the new way Spring wants us to call external APIs &#8211; not considering external libs like feign now. In Spring Security there is also an integration for OAuth2/OIDC compliant authentication. Taking the application mentioned above you need to have 2 dependencies in place:</p>



<pre class="wp-block-code"><code>    implementation("org.springframework.boot:spring-boot-starter-security")
    implementation("org.springframework.security:spring-security-oauth2-client")</code></pre>



<p>In your config you need to configure the OIDC server:</p>



<pre class="wp-block-code"><code>spring:
  security:
    oauth2:
      client:
        registration:
          keycloak:
            client-id: &lt;id>
            client-secret: &lt;secret>
            authorization-grant-type: client_credentials
        provider:
          keycloak:
            token-uri: &lt;server-url>/realms/&lt;realm>/protocol/openid-connect/token</code></pre>



<p>In the method you create your WebClient you need to add an oauth filter:</p>



<pre class="wp-block-code"><code>    @Bean
    @Qualifier("keycloak")
    @ConditionalOnMissingBean
    fun keycloakWebClient(
      clientRegistrations: ReactiveClientRegistrationRepository,
      authorizedClientService: ReactiveOAuth2AuthorizedClientService,
    ): WebClient {

        val oauth = ServerOAuth2AuthorizedClientExchangeFilterFunction(
                AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
                        clientRegistrations, authorizedClientService
                )
        );
        oauth.setDefaultClientRegistrationId("keycloak");

        return WebClient.builder()
            .exchangeStrategies(ExchangeStrategies
                .builder()
                .codecs { clientDefaultCodecsConfigurer: ClientCodecConfigurer ->
                    clientDefaultCodecsConfigurer.defaultCodecs()
                        .jackson2JsonEncoder(Jackson2JsonEncoder(jsonMapper, MediaType.APPLICATION_JSON))
                    clientDefaultCodecsConfigurer.defaultCodecs()
                        .jackson2JsonDecoder(Jackson2JsonDecoder(jsonMapper, MediaType.APPLICATION_JSON))
                }.build()
            )
            .filter(WebClientLoggerBuilder.logRequest())
            .filter(oauth)
            .build()
    }</code></pre>



<p>This results in a WebClient instance using OAuth2 authentication with the server you configured for all calls you use it for. The mapping &#8220;keycloak&#8221; refers to the key &#8220;keycloak&#8221; in your application.yml&#8217;s <code>spring.security.oauth2.client.registration</code> and <code>spring.security.oauth2.client.provider</code>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Spring Boot WebClient with request logging</title>
		<link>https://blog.mbo.dev/archives/1904</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Mon, 30 Jan 2023 17:36:17 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Kotlin]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1904</guid>

					<description><![CDATA[RestTemplate is dead and everybody should use WebClient now. That&#8217;s what you find everywhere so I had a look how to configure an instance of WebClient properly: This defines a bean genericWebClient with Spring Boot configured Jackson ObjectMapper instance. Here is also the code for the WebClientLoggerBuilder: This simply logs method, path and headers of [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>RestTemplate is dead and everybody should use WebClient now. That&#8217;s what you find everywhere so I had a look how to configure an instance of WebClient properly:</p>



<pre class="wp-block-code"><code>import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
import org.springframework.http.codec.ClientCodecConfigurer
import org.springframework.http.codec.json.Jackson2JsonDecoder
import org.springframework.http.codec.json.Jackson2JsonEncoder
import org.springframework.web.reactive.function.client.ExchangeStrategies
import org.springframework.web.reactive.function.client.WebClient

@Configuration
class WebClientConfig(
    private val jsonMapper: ObjectMapper,
) {

    @Bean
    @ConditionalOnMissingBean
    fun genericWebClient(): WebClient {
        return WebClient.builder()
            .exchangeStrategies(ExchangeStrategies
                .builder()
                .codecs { clientDefaultCodecsConfigurer: ClientCodecConfigurer -&gt;
                    clientDefaultCodecsConfigurer.defaultCodecs()
                        .jackson2JsonEncoder(Jackson2JsonEncoder(jsonMapper, MediaType.APPLICATION_JSON))
                    clientDefaultCodecsConfigurer.defaultCodecs()
                        .jackson2JsonDecoder(Jackson2JsonDecoder(jsonMapper, MediaType.APPLICATION_JSON))
                }.build()
            )
            .filter(WebClientLoggerBuilder.logRequest())
            .build()
    }
}</code></pre>



<p>This defines a bean genericWebClient with Spring Boot configured Jackson ObjectMapper instance.</p>



<p>Here is also the code for the WebClientLoggerBuilder:</p>



<pre class="wp-block-code"><code>import org.springframework.web.reactive.function.client.ClientRequest
import org.springframework.web.reactive.function.client.ExchangeFilterFunction
import reactor.core.publisher.Mono
import java.util.function.Consumer
import org.slf4j.Logger
import org.slf4j.LoggerFactory

object WebClientLoggerBuilder {

    private val log: Logger = LoggerFactory.getLogger(javaClass)

    fun logRequest(): ExchangeFilterFunction {
        return ExchangeFilterFunction.ofRequestProcessor { clientRequest: ClientRequest -&gt;
            if (log.isDebugEnabled) {
                val sb = StringBuilder("Request: \n")
                    .append(clientRequest.method())
                    .append(" ")
                    .append(clientRequest.url())
                    .append("\n")
                clientRequest.headers()
                    .forEach { name: String?, values: List&lt;String?&gt; -&gt;
                        values.forEach(
                            Consumer { value: String? -&gt; sb.append(name).append(": ").append(value).append("\n") })
                    }
                log.debug(sb.toString())
            }
            Mono.just(clientRequest)
        }
    }
}</code></pre>



<p>This simply logs method, path and headers of a request if debug log is enabled and added to the WebClient. Executing a blocking call that parses JSON to a list of objects is then quite easy:</p>



<pre class="wp-block-code"><code>fun &lt;T&gt; queryListWithoutBody(
    path: String,
    listClass: Class&lt;T&gt;,
    params: MultiValueMap&lt;String, String&gt;
): List&lt;T&gt; {
    return webClient.get()
        .uri { it.path(path).queryParams(params).build() }
        .retrieve()
        .bodyToFlux(listClass)
        .collectList()
        .block() ?: emptyList()
}</code></pre>



<p>With this code you&#8217;re able to replace RestTemplate without going reactive. Adding spring-boot-starter-webflux also won&#8217;t enable netty if you also have good old spring-boot-starter-web in the classpath.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>duplicate but no duplicate handling strategy has been set &#8211; gradle DuplicationStrategy</title>
		<link>https://blog.mbo.dev/archives/1874</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Mon, 31 Oct 2022 23:15:20 +0000</pubDate>
				<category><![CDATA[gradle]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Kotlin]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1874</guid>

					<description><![CDATA[While using Java and Kotlin with Spring Boot the following exception occurs and it&#8217;s quite hard to find how to fix it. The simple solution is to add the following top level config in your gradle file:]]></description>
										<content:encoded><![CDATA[
<p>While using Java and Kotlin with Spring Boot the following exception occurs and it&#8217;s quite hard to find how to fix it.</p>



<pre class="wp-block-code"><code>1: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':app:processResources'.
> Entry ... is a duplicate but no duplicate handling strategy has been set. Please refer to https://docs.gradle.org/7.5.1/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:duplicatesStrategy for details.
</code></pre>



<p>The simple solution is to add the following top level config in your gradle file:</p>



<pre class="wp-block-code"><code>tasks.withType&lt;Copy> {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}</code></pre>



<p></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Local OpenFaaS environment with minikube and docker driver</title>
		<link>https://blog.mbo.dev/archives/1868</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Fri, 28 Oct 2022 17:37:51 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1868</guid>

					<description><![CDATA[Following the documentation using arkade was (as stated in the docs) the fastest and easiest way to get a working local installation running. There are numerous outdated tutorials about how to run OpenFaaS locally but none of really worked in my envrionment. I am using a MacBook M1 Max with docker driver. I have docker [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Following the documentation using arkade was (as stated in the docs) the fastest and easiest way to get a working local installation running. There are numerous outdated tutorials about how to run OpenFaaS locally but none of really worked in my envrionment. I am using a MacBook M1 Max with docker driver. I have docker 20.10.17, faas-cli 0.14.11 and minikube 1.27.1 installed via brew. I followed <a rel="noreferrer noopener" href="https://docs.openfaas.com/deployment/kubernetes/#1-deploy-the-chart-with-arkade-fastest-option" target="_blank">https://docs.openfaas.com/deployment/kubernetes/#1-deploy-the-chart-with-arkade-fastest-option</a>, installed arkade 0.8.48 via brew and run <code>arkade install openfaas</code>. After a short moment all services were up and running and with port 8080 forwarded to localhost I was able to connect to openfaas.</p>



<p>Here a summary of commands if you have everything installed and want to bring up minikube with openfaas:</p>



<pre class="wp-block-code"><code>minikube start
arkade install openfaas

# wait until the following command shows all services as ready:
kubectl -n openfaas get deployments -l "release=openfaas, app=openfaas"

kubectl rollout status -n openfaas deploy/gateway
kubectl port-forward -n openfaas svc/gateway 8080:8080 &amp;

PASSWORD=$(kubectl get secret -n openfaas basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 --decode; echo)
echo -n $PASSWORD | faas-cli login --username admin --password-stdin

# check if the service answers:
faas-cli list

# print password
echo $PASSWORD</code></pre>



<p>After this you can open http://localhost:8080 with admin user and above printed password.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
