<?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>Java &#8211; Manuel Bogner&#039;s Blog</title>
	<atom:link href="https://blog.mbo.dev/archives/category/dev/java/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.mbo.dev</link>
	<description>Solutions to everyday IT problems</description>
	<lastBuildDate>Wed, 15 Nov 2023 15:01:22 +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>Java &#8211; Manuel Bogner&#039;s Blog</title>
	<link>https://blog.mbo.dev</link>
	<width>32</width>
	<height>32</height>
</image> 
	<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>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>java.lang.ClassNotFoundException: javax.annotation.PostConstruct</title>
		<link>https://blog.mbo.dev/archives/1860</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Wed, 21 Sep 2022 09:38:55 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JEE]]></category>
		<category><![CDATA[Kotlin]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1860</guid>

					<description><![CDATA[When upgrading to the latest tools and libraries in a Spring Boot application with OpenAPI code generator in place the OpenAPI Kotlin generator introduced jakarta.annotation:jakarta.annotation-api. But with this in place the application refused to start with something like org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat…Caused by: java.lang.IllegalStateException: [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>When upgrading to the latest tools and libraries in a Spring Boot application with OpenAPI code generator in place the OpenAPI Kotlin generator introduced <strong><em>jakarta.annotation:jakarta.annotation-api</em></strong>. But with this in place the application refused to start with something like</p>



<p><code><em>org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat<br>…<br>Caused by: java.lang.IllegalStateException: StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[] failed to start</em></code></p>



<p>Digging a bit deeper I found the real reason:</p>



<p><code><em>java.lang.NoClassDefFoundError: javax/annotation/PostConstruct</em></code></p>



<p>This can be fixed by also including <strong><em>javax.annotation:javax.annotation-api</em></strong> as a dependency. The new jakarta based library only includes <em>jakarta.annotation.*</em> but not <em>javax.annotation.*</em>. But Spring Boot still requires the old namespace to work. I didn&#8217;t dig deeper why including the dependency broke the app though it was working before without both of the dependencies.</p>



<p>Another great sample for how convenient this rename is and further will be.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Lombok and log4shell</title>
		<link>https://blog.mbo.dev/archives/1747</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Wed, 29 Dec 2021 14:08:45 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1747</guid>

					<description><![CDATA[Project lombok has become a defacto standard in close to every Java project I see. Because of this I want to also write about the ongoing log4j problems as described under https://blog.coffeebeans.at/archives/1709. The annotation @Log4j allows to create a log4j logger field in the annotated class. This of course implicates that your project is using [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Project lombok has become a defacto standard in close to every Java project I see. Because of this I want to also write about the ongoing log4j problems as described under <a href="https://blog.coffeebeans.at/archives/1709" target="_blank" rel="noreferrer noopener">https://blog.coffeebeans.at/archives/1709</a>.</p>



<p>The annotation @Log4j allows to create a log4j logger field in the annotated class. This of course implicates that your project is using Log4j for logging and could make your application vulnerable to log4shell if not updated to latest release.</p>



<p>I would highly recommend using @Slf4j instead of @Log4j and use the <strong>S</strong>imple <strong>L</strong>ogging <strong>F</strong>acade <strong>4</strong> <strong>J</strong>ava functionality to be able to replace the logging implementation by simply changing some configs and/or libraries instead of changing your code for doing so.</p>



<p>Sample with @Log4j:</p>



<pre class="wp-block-code"><code>@Log4j
class SampleLog4j {

  public void soSomething() {
    log.debug("this is a log4j log message");
  }

}</code></pre>



<p>The same sample using slf4j:</p>



<pre class="wp-block-code"><code><meta charset="utf-8">@Slf4j
class SampleLog4j {

  public void soSomething() {
    log.debug("this is a slf4j log message");
  }

}</code></pre>



<p>As you can see for simple string messages there is no difference at all. A big difference comes in place when it comes to parameterised log messages which log like the following with slf4j:</p>



<pre class="wp-block-code"><code><meta charset="utf-8">log.error("this is a {} error message", someVar, exception);</code></pre>



<p>Taken from my knowledge with log4j (which was going down over the years of using slf4j already) there would be a way with ParameterizedMessage or simple String.format to achieve the same:</p>



<pre class="wp-block-code"><code><meta charset="utf-8"><meta charset="utf-8">// with String.format
log.<meta charset="utf-8">error(String.format("this is a %s error message", <meta charset="utf-8">someVar), <meta charset="utf-8">exception);

// or string concatenation
<meta charset="utf-8">log.<meta charset="utf-8">error("this is a " + <meta charset="utf-8">someVar + " error message", <meta charset="utf-8">exception);

// or with <meta charset="utf-8">ParameterizedMessage
log.error(new ParameterizedMessage("<meta charset="utf-8">this is a {} error message", <meta charset="utf-8">someVar), exception);</code></pre>



<p>I don&#8217;t think that <meta charset="utf-8">ParameterizedMessage is really used a lot and most people rely on string concatenation or String.format. From that perspective: Biggest difference are the used log levels: Slf4j doesn&#8217;t know the fatal log level &#8211; see <a rel="noreferrer noopener" href="https://www.slf4j.org/faq.html#fatal" target="_blank">https://www.slf4j.org/faq.html#fatal</a>. If you&#8217;re happy the do some changes on that you should be able to replace @Log4j with @Slf4j after having taken care about fatal. Of course this assumes you&#8217;re doing nothing fancy with your logger ;-) Also checkout the migrator tool: <a href="https://www.slf4j.org/migrator.html" target="_blank" rel="noreferrer noopener">https://www.slf4j.org/migrator.html</a>.</p>



<p>If you&#8217;re in a JEE environment with jboss/wildfly/eap (or simply used to it) also @JBossLog could be interesting to you which would generate a jboss-logger instance for you that would also not be vulnerable to log4shell but is also naming a logging implementation directly in your code instead of using a facade.</p>



<p>Checkout the lombok external logger implementations yourself under <a rel="noreferrer noopener" href="https://projectlombok.org/api/index.html" target="_blank">https://projectlombok.org/api/index.html</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>gradle status plugin</title>
		<link>https://blog.mbo.dev/archives/1718</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Sat, 18 Dec 2021 17:04:04 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1718</guid>

					<description><![CDATA[I&#8217;ve started to write a simple plugin to analyse gradle projects and send dependency information to a central server. This way I want to get insight into all projects in the company and check if there is some log4j2 dependency below 2.16.0. You can find the code under https://github.com/mbogner/gradle-status-plugin. The plugin was accepted and is [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>I&#8217;ve started to write a simple plugin to analyse gradle projects and send dependency information to a central server. This way I want to get insight into all projects in the company and check if there is some log4j2 dependency below 2.16.0. You can find the code under <a rel="noreferrer noopener" href="https://github.com/mbogner/gradle-status-plugin" target="_blank">https://github.com/mbogner/gradle-status-plugin</a>. The plugin was accepted and is available via <a rel="noreferrer noopener" href="https://plugins.gradle.org/plugin/dev.mbo.gradlestatusplugin" target="_blank">https://plugins.gradle.org/plugin/dev.mbo.gradlestatusplugin</a>. A web endpoint is still WIP. If you&#8217;re interested, an example json string that is posted to that endpoint looks like this:</p>



<pre class="wp-block-code"><code>{
  "group": "dev.mbo",
  "name": "gradle-status-test",
  "version": "1.0.0",
  "dependencies": {
    "org.apache.commons:commons-lang3:3.12.0": {
      "group": "org.apache.commons",
      "name": "commons-lang3",
      "version": "3.12.0",
      "project": false,
      "configurations": &#91;
        "compileClasspath",
        "default",
        "runtimeClasspath",
        "testCompileClasspath",
        "testRuntimeClasspath"
      ]
    },
    "commons-io:commons-io:2.11.0": {
      "group": "commons-io",
      "name": "commons-io",
      "version": "2.11.0",
      "project": false,
      "configurations": &#91;
        "compileClasspath",
        "default",
        "runtimeClasspath",
        "testCompileClasspath",
        "testRuntimeClasspath"
      ]
    },
    "gradle-status-test:module2:1.0.0": {
      "group": "gradle-status-test",
      "name": "module2",
      "version": "1.0.0",
      "project": true,
      "configurations": &#91;
        "compileClasspath",
        "default",
        "implementation",
        "runtimeClasspath",
        "runtimeElements",
        "testCompileClasspath",
        "testImplementation",
        "testRuntimeClasspath"
      ]
    },
    "com.fasterxml.jackson.core:jackson-databind:2.13.0": {
      "group": "com.fasterxml.jackson.core",
      "name": "jackson-databind",
      "version": "2.13.0",
      "project": false,
      "configurations": &#91;
        "default",
        "runtimeClasspath",
        "testRuntimeClasspath"
      ]
    },
    "com.fasterxml.jackson.core:jackson-annotations:2.13.0": {
      "group": "com.fasterxml.jackson.core",
      "name": "jackson-annotations",
      "version": "2.13.0",
      "project": false,
      "configurations": &#91;
        "default",
        "runtimeClasspath",
        "testRuntimeClasspath"
      ]
    },
    "com.fasterxml.jackson:jackson-bom:2.13.0": {
      "group": "com.fasterxml.jackson",
      "name": "jackson-bom",
      "version": "2.13.0",
      "project": false,
      "configurations": &#91;
        "default",
        "runtimeClasspath",
        "testRuntimeClasspath"
      ]
    },
    "com.fasterxml.jackson.core:jackson-core:2.13.0": {
      "group": "com.fasterxml.jackson.core",
      "name": "jackson-core",
      "version": "2.13.0",
      "project": false,
      "configurations": &#91;
        "default",
        "runtimeClasspath",
        "testRuntimeClasspath"
      ]
    },
    "org.junit:junit-bom:5.8.2": {
      "group": "org.junit",
      "name": "junit-bom",
      "version": "5.8.2",
      "project": false,
      "configurations": &#91;
        "testCompileClasspath",
        "testRuntimeClasspath"
      ]
    },
    "org.junit.jupiter:junit-jupiter-api:5.8.2": {
      "group": "org.junit.jupiter",
      "name": "junit-jupiter-api",
      "version": "5.8.2",
      "project": false,
      "configurations": &#91;
        "testCompileClasspath",
        "testRuntimeClasspath"
      ]
    },
    "org.opentest4j:opentest4j:1.2.0": {
      "group": "org.opentest4j",
      "name": "opentest4j",
      "version": "1.2.0",
      "project": false,
      "configurations": &#91;
        "testCompileClasspath",
        "testRuntimeClasspath"
      ]
    },
    "org.junit.platform:junit-platform-commons:1.8.2": {
      "group": "org.junit.platform",
      "name": "junit-platform-commons",
      "version": "1.8.2",
      "project": false,
      "configurations": &#91;
        "testCompileClasspath",
        "testRuntimeClasspath"
      ]
    },
    "org.apiguardian:apiguardian-api:1.1.2": {
      "group": "org.apiguardian",
      "name": "apiguardian-api",
      "version": "1.1.2",
      "project": false,
      "configurations": &#91;
        "testCompileClasspath"
      ]
    },
    "org.junit.jupiter:junit-jupiter-engine:5.8.2": {
      "group": "org.junit.jupiter",
      "name": "junit-jupiter-engine",
      "version": "5.8.2",
      "project": false,
      "configurations": &#91;
        "testRuntimeClasspath"
      ]
    },
    "org.junit.platform:junit-platform-engine:1.8.2": {
      "group": "org.junit.platform",
      "name": "junit-platform-engine",
      "version": "1.8.2",
      "project": false,
      "configurations": &#91;
        "testRuntimeClasspath"
      ]
    }
  },
  "subprojects": &#91;
    {
      "group": "gradle-status-test",
      "name": "module1",
      "version": "1.0.0",
      "dependencies": {
        "com.fasterxml.jackson.core:jackson-databind:2.13.0": {
          "group": "com.fasterxml.jackson.core",
          "name": "jackson-databind",
          "version": "2.13.0",
          "project": false,
          "configurations": &#91;
            "compileClasspath",
            "default",
            "runtimeClasspath",
            "testCompileClasspath",
            "testRuntimeClasspath"
          ]
        },
        "com.fasterxml.jackson.core:jackson-annotations:2.13.0": {
          "group": "com.fasterxml.jackson.core",
          "name": "jackson-annotations",
          "version": "2.13.0",
          "project": false,
          "configurations": &#91;
            "compileClasspath",
            "default",
            "runtimeClasspath",
            "testCompileClasspath",
            "testRuntimeClasspath"
          ]
        },
        "com.fasterxml.jackson:jackson-bom:2.13.0": {
          "group": "com.fasterxml.jackson",
          "name": "jackson-bom",
          "version": "2.13.0",
          "project": false,
          "configurations": &#91;
            "compileClasspath",
            "default",
            "runtimeClasspath",
            "testCompileClasspath",
            "testRuntimeClasspath"
          ]
        },
        "com.fasterxml.jackson.core:jackson-core:2.13.0": {
          "group": "com.fasterxml.jackson.core",
          "name": "jackson-core",
          "version": "2.13.0",
          "project": false,
          "configurations": &#91;
            "compileClasspath",
            "default",
            "runtimeClasspath",
            "testCompileClasspath",
            "testRuntimeClasspath"
          ]
        }
      },
      "subprojects": &#91;]
    },
    {
      "group": "gradle-status-test",
      "name": "module2",
      "version": "1.0.0",
      "dependencies": {
        "org.apache.commons:commons-lang3:3.12.0": {
          "group": "org.apache.commons",
          "name": "commons-lang3",
          "version": "3.12.0",
          "project": false,
          "configurations": &#91;
            "compileClasspath",
            "default",
            "runtimeClasspath",
            "testCompileClasspath",
            "testRuntimeClasspath"
          ]
        },
        "gradle-status-test:module1:1.0.0": {
          "group": "gradle-status-test",
          "name": "module1",
          "version": "1.0.0",
          "project": true,
          "configurations": &#91;
            "compileClasspath",
            "default",
            "implementation",
            "runtimeClasspath",
            "runtimeElements",
            "testCompileClasspath",
            "testImplementation",
            "testRuntimeClasspath"
          ]
        },
        "com.fasterxml.jackson.core:jackson-databind:2.13.0": {
          "group": "com.fasterxml.jackson.core",
          "name": "jackson-databind",
          "version": "2.13.0",
          "project": false,
          "configurations": &#91;
            "default",
            "runtimeClasspath",
            "testRuntimeClasspath"
          ]
        },
        "com.fasterxml.jackson.core:jackson-annotations:2.13.0": {
          "group": "com.fasterxml.jackson.core",
          "name": "jackson-annotations",
          "version": "2.13.0",
          "project": false,
          "configurations": &#91;
            "default",
            "runtimeClasspath",
            "testRuntimeClasspath"
          ]
        },
        "com.fasterxml.jackson:jackson-bom:2.13.0": {
          "group": "com.fasterxml.jackson",
          "name": "jackson-bom",
          "version": "2.13.0",
          "project": false,
          "configurations": &#91;
            "default",
            "runtimeClasspath",
            "testRuntimeClasspath"
          ]
        },
        "com.fasterxml.jackson.core:jackson-core:2.13.0": {
          "group": "com.fasterxml.jackson.core",
          "name": "jackson-core",
          "version": "2.13.0",
          "project": false,
          "configurations": &#91;
            "default",
            "runtimeClasspath",
            "testRuntimeClasspath"
          ]
        }
      },
      "subprojects": &#91;]
    }
  ]
}</code></pre>



<p>The structure under subprojects is simply the same but nested recursively. `project=true` means that the dependecy is a project(&#8220;:name&#8221;) dependency.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>JSON in PostgreSQL with application side validation</title>
		<link>https://blog.mbo.dev/archives/1703</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Mon, 06 Dec 2021 17:36:25 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1703</guid>

					<description><![CDATA[I was looking for a way to validate JSON data structure with the schema saved in the database. PostgreSQL has a very cool way to store json in JSONB fields but the question was how to validate this without extra PostgreSQL plugins and integrated in hibernate&#8217;s lifecycle with bean validation. Under https://github.com/mbogner/schema-validator I came up [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>I was looking for a way to validate JSON data structure with the schema saved in the database. PostgreSQL has a very cool way to store json in JSONB fields but the question was how to validate this without extra PostgreSQL plugins and integrated in hibernate&#8217;s lifecycle with bean validation.</p>



<p>Under https://github.com/mbogner/schema-validator I came up with a solution that enables you to validate against a JSON Schema or against a custom map that holds schema information. Both values can be stored in the database and validate a map that can be used to map JSONB fields. It is based on a javax.validation class level validator that allows you to have your json as a map and your schema in a second field.</p>



<p>Checkout <a href="https://github.com/mbogner/schema-validator" target="_blank" rel="noreferrer noopener">https://github.com/mbogner/schema-validator</a> to see the code. I would be happy to hear your opinion or other ways to reach the goal in a maybe easier way.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
