Configuring Spring Boot 3 Behind Nginx Reverse Proxy

Recently got a very unique usecase where nginx is redirecting request to a different port that is provided by Nginx. Based on below image, we can see that user is accessing port 8080 on Nginx but getting HTTP code 302 redirect to port 8081 which is not exposed by Nginx.

Workaround is quite straighforward, we can set this configuration on Spring Boot’s application properties.

server.forward-headers-strategy=FRAMEWORK

while having this configuration on nginx.conf

        location / {
                proxy_pass http://127.0.0.1:8081;
                proxy_http_version 1.1;
                proxy_set_header Connection "";
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
				proxy_set_header X-Forwarded-Port  $server_port;
                proxy_set_header X-Forwarded-Host  $host;
				
                proxy_busy_buffers_size 512k;
                proxy_buffers 8 512k;
                proxy_buffer_size 256k;
                proxy_read_timeout 1800;
                proxy_connect_timeout 1800;
                proxy_send_timeout 1800;
                client_max_body_size 50M;
                proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
                proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
                proxy_ssl_ciphers HIGH:!aNULL:!MD5;
                proxy_ssl_verify off;
                proxy_set_header cookie $http_cookie;

                port_in_redirect off;
                absolute_redirect off;
        }

We can use this Java project for testing,

https://github.com/edwin/spring-3-keycloak

Creating Java 21 Runtime on top of an UBI Base Image

Just recently had a request to create a custom image based on UBI but extendable, means able to be installed custom package for other functionality.

For this sample, im using UBI9 latest version which is 9.4. We can find our Dockerfile below,

FROM registry.access.redhat.com/ubi9/ubi-minimal:9.4

LABEL BASE_IMAGE="registry.access.redhat.com/ubi9/ubi-minimal:9.4"
LABEL JAVA_VERSION="21"

ENV LANGUAGE='en_US:en'
ENV TZ='Asia/Jakarta'

RUN microdnf install -y --nodocs java-21-openjdk-headless  \
    && microdnf clean all  \
    && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security

WORKDIR /work/

COPY --chown=185 target/quarkus-app/lib/ /work/lib/
COPY --chown=185 target/quarkus-app/*.jar /work/application.jar
COPY --chown=185 target/quarkus-app/app/ /work/app/
COPY --chown=185 target/quarkus-app/quarkus/ /work/quarkus/

ENV JAVA_OPTS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager -XX:TieredStopAtLevel=1 -noverify -XX:+UseShenandoahGC -XX:+AlwaysPreTouch -XX:+UseNUMA -Xlog:gc*,safepoint=debug:file=/tmp/gc.log.%p:time,uptime:filecount=5,filesize=50M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/"

EXPOSE 8080
USER 185

CMD java $JAVA_OPTS -jar application.jar

Based on above script, we are using UBI9 and installing some packages using microdnf with a custom JAVA_OPTS variables.

Code can be found on this URL,

https://github.com/edwin/quarkus-and-java21

Compress and Deleting AMQ Broker Logs Regularly

AMQ Broker, or its Open Source product which is ActiveMQ Artemis, can generate a log to a specific file and can be use for auditing purpose. However sometimes we ned to maintain some log retentions by archieving and deleting logs after some period of time to prevent logs filling up our storage.

We can achieve that by using log4j2 configuration which is comes out of the box in AMQ instance. For example, this configuration below would create a new log file every 5 minutes, compress old logs, and deleting logs that is older than 10 minutes.

# Log file appender
appender.log_file.type = RollingFile
appender.log_file.name = log_file
appender.log_file.fileName = ${sys:artemis.instance}/log/artemis.log
appender.log_file.filePattern = ${sys:artemis.instance}/log/artemis.log.%d{yyyyMMdd.HHmm}.gz
appender.log_file.layout.type = PatternLayout
appender.log_file.layout.pattern = %d %-5level [%logger] %msg%n
appender.log_file.policies.type = Policies
appender.log_file.policies.cron.type = CronTriggeringPolicy
appender.log_file.policies.cron.schedule = 0 */5 * ? * *
appender.log_file.policies.cron.evaluateOnStartup = true
appender.log_file.strategy.type = DefaultRolloverStrategy
appender.log_file.strategy.action.type = Delete
appender.log_file.strategy.action.basePath = ${sys:artemis.instance}/log/
appender.log_file.strategy.action.maxDepth = 1
appender.log_file.strategy.action.condition.type = IfFileName
appender.log_file.strategy.action.condition.glob = artemis.log.*.gz
appender.log_file.strategy.action.ifAny.type = IfAny
appender.log_file.strategy.action.ifAny.ifLastModified.type = IfLastModified
appender.log_file.strategy.action.ifAny.ifLastModified.age = PT10M

We can change 10minutes period into something else by changing the value of “age” following ISO-8601 duration format,

https://logging.apache.org/log4j/2.x/javadoc/log4j-core/org/apache/logging/log4j/core/appender/rolling/action/Duration.html#parse(java.lang.CharSequence)

While log compressing, can be modified by changing the “cron schedule” value,

https://logging.apache.org/log4j/2.12.x/log4j-core/apidocs/org/apache/logging/log4j/core/util/CronExpression.html

Make Spring Boot Starting Time Faster

When working in a cloud environment, startup time is something that is important especially when you application is relying in HPA (Horizontal Pod Autoscale). This means that an application that can start faster is better since they can handle traffic sooner compared when having a slower startup time.

Lets take my sample Spring Boot and Camel project,

https://github.com/edwin/spring-boot-camel-json-and-wsdl

in a normal condition, this application can take almost 10second to starting-up

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.2.5)

2024-06-22T20:24:06.196+07:00  INFO 12328 --- [           main] com.edw.Main                             : Starting Main using Java 17.0.6 with PID 12328 (spring-boot-camel-json-and-wsdl-1.0.jar started by edwin in /tmp/spring-boot-camel-json-and-wsdl)
2024-06-22T20:24:06.198+07:00  INFO 12328 --- [           main] com.edw.Main                             : No active profile set, falling back to 1 default profile: "default"
.......
2024-06-22T20:24:14.718+07:00  INFO 12328 --- [           main] o.a.c.impl.engine.AbstractCamelContext   : Apache Camel 4.4.0.redhat-00019 (camel-testing) started in 1s623ms (build:0ms init:0ms start:1s623ms)
2024-06-22T20:24:14.726+07:00  INFO 12328 --- [           main] com.edw.Main                             : Started Main in 9.116 seconds (process running for 9.692)

We can see from above logs that it needs 9second to starting. But some might not see this as ideal, and start looking for some areas of improvements. And we can start by creating our Spring Boot as Lazy-Loading using below application.properties configuration

spring.main.lazy-initialization=true

Adding below Java Variables also help to increase the speed of starting up

 -XX:TieredStopAtLevel=1 -noverify

Which finally gives us this Java command

$ java -jar  -XX:TieredStopAtLevel=1 -noverify .\target\spring-boot-camel-json-and-wsdl-1.0.jar

and we can see from below logs, starting time is literally reduced into 5seconds, which is good enough.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.2.5)

2024-06-22T20:31:54.155+07:00  INFO 2512 --- [           main] com.edw.Main                             : Starting Main using Java 17.0.6 with PID 12328 (spring-boot-camel-json-and-wsdl-1.0.jar started by edwin in /tmp/spring-boot-camel-json-and-wsdl)
2024-06-22T20:31:54.182+07:00  INFO 2512 --- [           main] com.edw.Main                             : No active profile set, falling back to 1 default profile: "default"
........
2024-06-22T20:31:59.231+07:00  INFO 2512 --- [           main] o.a.c.impl.engine.AbstractCamelContext   : Apache Camel 4.4.0.redhat-00019 (camel-testing) started in 931ms (build:0ms init:0ms start:931ms)
2024-06-22T20:31:59.359+07:00  INFO 2512 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 2 endpoint(s) beneath base path '/actuator'
2024-06-22T20:31:59.375+07:00  INFO 2512 --- [           main] com.edw.Main                             : Started Main in 5.759 seconds (process running for 6.201)

Bringing Back the Old IntelliJ Theme Back

The latest version of IntelliJ having a very strong VSCode ambience in it, which is perhaps good for some people but perhaps not my cup of tea.

Bringing the old theme back is pretty much straight forward, we can do it by pressing Shift twice and write “switch to classic UI” there, bringing the old legacy theme back.