<?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>Uncategorized &#8211; Manuel Bogner&#039;s Blog</title>
	<atom:link href="https://blog.mbo.dev/archives/category/uncategorized/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.mbo.dev</link>
	<description>Solutions to everyday IT problems</description>
	<lastBuildDate>Tue, 05 Mar 2024 13:10:24 +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>Uncategorized &#8211; Manuel Bogner&#039;s Blog</title>
	<link>https://blog.mbo.dev</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>How to Replace Spring Dependency Management Gradle Plugin with a Version Catalog</title>
		<link>https://blog.mbo.dev/archives/2018</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Tue, 05 Mar 2024 13:10:22 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=2018</guid>

					<description><![CDATA[Are you looking for a cleaner alternative to managing dependencies in your Spring projects? While the Spring Dependency Management Gradle Plugin offers convenience, it may sometimes lead to issues such as transitive dependency conflicts. Enter the version catalog—a more streamlined solution. Let&#8217;s dive into how you can replace the Spring Dependency Management Gradle Plugin with [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Are you looking for a cleaner alternative to managing dependencies in your Spring projects? While the Spring Dependency Management Gradle Plugin offers convenience, it may sometimes lead to issues such as transitive dependency conflicts. Enter the version catalog—a more streamlined solution. Let&#8217;s dive into how you can replace the Spring Dependency Management Gradle Plugin with a version catalog for smoother dependency management in your Spring projects.</p>



<h3 class="wp-block-heading">Why Replace the Spring Dependency Management Gradle Plugin?</h3>



<p>The Spring Dependency Management Gradle Plugin simplifies dependency management by providing a BOM (Bill of Materials) file that declares dependency versions. However, it can sometimes result in transitive dependency conflicts or inadvertently using outdated versions. By leveraging a version catalog, you gain greater control and flexibility over your project dependencies.</p>



<h3 class="wp-block-heading">Creating a Version Catalog</h3>



<p>Start by creating a <code>libs.versions.toml</code> file in the <code>gradle</code> folder of your project. This file will serve as your version catalog, containing sections for versions, libraries, and plugins. Here&#8217;s how it might look:</p>



<pre class="wp-block-code"><code>&#91;versions]
# libs
flyway = "10.8.1" # https://mvnrepository.com/artifact/org.flywaydb/flyway-core
# plugins
spring-boot = "3.2.3" # https://spring.io/projects/spring-boot
kotlin = "1.9.22" # https://kotlinlang.org/docs/releases.html#release-details
sonarqube = "4.3.1.3277" # https://plugins.gradle.org/plugin/org.sonarqube

&#91;libraries]
org-flywaydb-core = { module = "org.flywaydb:flyway-core", version.ref = "flyway" }
org-flywaydb-database-postgresql = { module = "org.flywaydb:flyway-database-postgresql", version.ref = "flyway" }

&#91;plugins]
org-springframework-boot = { id = "org.springframework.boot", version.ref = "spring-boot" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-spring = { id = "org.jetbrains.kotlin.plugin.spring", version.ref = "kotlin" }
kotlin-jpa = { id = "org.jetbrains.kotlin.plugin.jpa", version.ref = "kotlin" }
sonarqube = { id = "org.sonarqube", version.ref = "sonarqube" }
</code></pre>



<h3 class="wp-block-heading">Integrating Version Catalog in Gradle</h3>



<p>Now, let&#8217;s integrate the version catalog into your Gradle build using Kotlin DSL.</p>



<pre class="wp-block-code"><code>plugins {
    alias(libs.plugins.org.springframework.boot)
    alias(libs.plugins.kotlin.jvm)
    alias(libs.plugins.kotlin.spring)
    alias(libs.plugins.kotlin.jpa)

    alias(libs.plugins.sonarqube)
    jacoco
}

...

dependencies {
    implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES))
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-web")

    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")

    implementation(libs.org.sonarsource.sonar.ws)
    implementation(libs.swagger.annotations.v3)

    runtimeOnly("org.postgresql:postgresql")
    runtimeOnly(libs.org.flywaydb.core)
    runtimeOnly(libs.org.flywaydb.database.postgresql)

    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.springframework.boot:spring-boot-testcontainers")
    testImplementation("org.testcontainers:junit-jupiter")
    testImplementation("org.testcontainers:postgresql")
}</code></pre>



<p>So all versions are either coming from the Spring Boot BOM that is imported first or from the libs.versions.toml file which is taken in account automatically.</p>



<h3 class="wp-block-heading">Conclusion</h3>



<p>Replacing the Spring Dependency Management Gradle Plugin with a version catalog provides a cleaner and more flexible approach to dependency management in your Spring projects. By centralizing your dependency versions in a separate file, you gain more control and avoid potential conflicts or outdated versions. Give it a try in your next Spring project and experience smoother dependency management firsthand. Happy coding!</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Hibernate Second Level Cache with Spring Boot 3 and Redis</title>
		<link>https://blog.mbo.dev/archives/1919</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Tue, 07 Mar 2023 12:38:52 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1919</guid>

					<description><![CDATA[In my Spring Boot 3 application that hosts an API I wanted to have some entries highly used but rarely changed entries in a second level cache. In the past I was usind hibernate-ehcache but that project seems to be dead. The replacement for it is hibernate-jcache. First attempt was to use ehcache backing the [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>In my Spring Boot 3 application that hosts an API I wanted to have some entries highly used but rarely changed entries in a second level cache. In the past I was usind hibernate-ehcache but that project seems to be dead. The replacement for it is hibernate-jcache.</p>



<p>First attempt was to use ehcache backing the jcache but this results in jaxb exceptions as usual. I already have redis in the system and thought about using it as second level cache. I found that redisson provides an implementation for this.</p>



<p>The following dependencies needed to be added to my gradle file:</p>



<pre class="wp-block-preformatted"><em>implementation</em>("org.hibernate.orm:hibernate-jcache")<br>// https://mvnrepository.com/artifact/org.redisson/redisson-hibernate-6<br><em>implementation</em>("org.redisson:redisson-hibernate-6:3.20.0")</pre>



<p>With this in place the application.yml was updated to use it (I only kept the relevant part in this post):</p>



<pre class="wp-block-preformatted">spring:
  jpa:
    ...
    properties:
      hibernate:
        ...
        cache:
          use_second_level_cache: true
          use_query_cache: true
          region:
            factory_class: org.redisson.hibernate.RedissonRegionFactory
          redisson:
            <em># not setting config path uses classpath /redisson.yaml
</em><em>            # setting it requires a proper file path like shown below to use the same file
</em><em>            # config: api/src/main/resources/redisson.yaml</em>
      jakarta:
        persistence:
          sharedCache:
            mode: ENABLE_SELECTIVE</pre>



<p>And by adding the following to the entities it already worked:</p>



<pre class="wp-block-preformatted">import jakarta.persistence.Cacheable

@Entity
...
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.<em>READ_WRITE</em>)
public class ... { ... }</pre>
]]></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>Sony BLE custom remote for a6400 (ILCE-6400)</title>
		<link>https://blog.mbo.dev/archives/1855</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Sun, 28 Aug 2022 11:17:09 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1855</guid>

					<description><![CDATA[I found a quite interesting video about creating a custom bluetooth remote for Sony cameras. The video can be found on youtube. It also links the used source code which can be found on github. Testing with my Sony a6400 I found out that the used pairing advertisement codes look different than explained in the [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>I found a quite interesting video about creating a custom bluetooth remote for Sony cameras. The video can be found on <a rel="noreferrer noopener" href="https://www.youtube.com/watch?v=G_nyD2bTs7A" target="_blank">youtube</a>. It also links the used source code which can be found on <a rel="noreferrer noopener" href="https://github.com/coral/freemote" target="_blank">github</a>. Testing with my Sony a6400 I found out that the used pairing advertisement codes look different than explained in the video.</p>



<figure class="wp-block-table"><table><tbody><tr><td>Open for pairing</td><td>22ec00</td></tr><tr><td>Not open for pairing</td><td>22ac00</td></tr></tbody></table></figure>



<p>The 8000FF00-FF00-FFFF-FFFF-FFFFFFFFFFFF commands used with FF01 in the video also work with my a6400.</p>



<figure class="wp-block-table"><table><tbody><tr><td>0x0106</td><td>shutter released</td></tr><tr><td>0x0107</td><td>transition to focus</td></tr><tr><td>0x0108</td><td>hold focus</td></tr><tr><td>0x0109</td><td>shutter fully pressed</td></tr></tbody></table></figure>



<p>I also used the BlueSee software which is available via Apple App Store for free.</p>



<p>For getting it to work you first need to enable bluetooth and bluetooth remote. Further you need to pair the BlueSee software by going into pairing mode on the camera. The software will show pairing information which you just have to accept and from then on it worked.</p>



<p>With this it should be quite easy to build a custom remote based on a BLE capable chip like the suggested Adafruit nrf52840.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Spring Boot Hardening &#8211; Disclosed Version</title>
		<link>https://blog.mbo.dev/archives/1842</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Mon, 01 Aug 2022 12:05:32 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1842</guid>

					<description><![CDATA[The default configuration of Spring Boot tells quite a lot about errors and software versions used. This is a potential security leak and therefor should be avoided. One step is to get rid of server information from header and default error pages. Result With my suggested changes in place the default error result pages will [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>The default configuration of Spring Boot tells quite a lot about errors and software versions used. This is a potential security leak and therefor should be avoided.</p>



<p>One step is to get rid of server information from header and default error pages.</p>



<h2 class="wp-block-heading">Result</h2>



<p>With my suggested changes in place the default error result pages will look like this:</p>



<pre class="wp-block-code"><code>➜  ~ curl -i http://127.0.0.1:5000 
HTTP/1.1 404 
Content-Type: text/html;charset=utf-8
Content-Length: 79
Date: Mon, 01 Aug 2022 11:56:16 GMT
Server: disclosed

&lt;!doctype html>&lt;html lang="en">&lt;title>error&lt;/title>&lt;body>Ups! 404&lt;/body>&lt;/html></code></pre>



<h2 class="wp-block-heading">Header</h2>



<p>Open your application.yml or application.properties file and add the property <strong><em>server.server-header</em></strong> and set it to whatever you want to have in your header. In the sample above I simply used &#8220;disclosed&#8221;.</p>



<h2 class="wp-block-heading">Body</h2>



<p>The default body also tells the server software name and version. With tomcat this can be removed with the sample above by adding the following classes:</p>



<pre class="wp-block-code"><code>import org.apache.catalina.Container;
import org.apache.catalina.core.StandardHost;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class ErrorConfig {

    // https://docs.spring.io/spring-boot/docs/2.5.4/reference/htmlsingle/#howto-use-tomcat-legacycookieprocessor
    // https://github.com/spring-projects/spring-boot/issues/21257#issuecomment-745565376
    @Bean
    public WebServerFactoryCustomizer&lt;TomcatServletWebServerFactory> errorReportValveCustomizer() {
        return (factory) -> {
            factory.addContextCustomizers(context -> {
                final Container parent = context.getParent();
                if (parent instanceof StandardHost) {
                    // above class FQCN
                    ((StandardHost) parent).setErrorReportValveClass(CustomErrorReportValve.class.getName());
                }
            });
        };
    }

}</code></pre>



<pre class="wp-block-code"><code>import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ErrorReportValve;
import org.apache.coyote.ActionCode;
import org.apache.tomcat.util.ExceptionUtils;
import org.springframework.http.MediaType;

import java.io.IOException;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicBoolean;

// Converting this to Kotlin results in this class not being used.
public class CustomErrorReportValve extends ErrorReportValve {

    @Override
    protected void report(final Request request, final Response response, final Throwable throwable) {
        // ref: ErrorReportValve implementation

        final int statusCode = response.getStatus();

        // Do nothing on a 1xx, 2xx and 3xx status
        // Do nothing if anything has been written already
        // Do nothing if the response hasn't been explicitly marked as in error
        //    and that error has not been reported.
        if (statusCode &lt; 400 || response.getContentWritten() > 0 || !response.setErrorReported()) {
            return;
        }

        // If an error has occurred that prevents further I/O, don't waste time
        // producing an error report that will never be read
        final AtomicBoolean result = new AtomicBoolean(false);
        response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED, result);
        if (!result.get()) {
            return;
        }

        try {
            try {
                response.setContentType(MediaType.TEXT_HTML_VALUE);
                response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            } catch (final Throwable t) {
                ExceptionUtils.handleThrowable(t);
                if (container.getLogger().isDebugEnabled()) {
                    container.getLogger().debug("status.setContentType", t);
                }
            }
            final Writer writer = response.getReporter();
            if (writer != null) {
                // If writer is null, it's an indication that the response has
                // been hard committed already, which should never happen
                writer.write("&lt;!doctype html>&lt;html lang=\"en\">&lt;title>error&lt;/title>&lt;body>Ups! " + statusCode + "&lt;/body>&lt;/html>");
                response.finishResponse();
            }
        } catch (IOException | IllegalStateException e) {
            // Ignore
        }
    }
}</code></pre>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
