Deploying to Minikube with Helm

# Start Minikube
minikube start --cpus 4 --memory 8192

# Our Spring Boot app and 3rd party service (MongoDB, Kafka)
eval $(minikube docker-env)
./gradlew build -x test
docker build -t our-spring-boot-app:v1 .
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm upgrade --install bitnami-mongodb --set auth.rootPassword=root123 bitnami/mongodb -f ./infra/bitnami/mongodb/dev.yaml
helm upgrade --install bitnami-kafka bitnami/kafka -f ./infra/bitnami/kafka/dev.yaml
helm upgrade --install our-spring-boot-app ./infra/our-spring-boot-app -f ./infra/our-spring-boot-app/dev.yaml

# dev.yaml
image:
  repository: our-spring-boot-app
  tag: v1
  pullPolicy: Never
envs:
  PORT: 8080
  HEALTH_PORT: 8081
  SPRING_PROFILES_ACTIVE: dev
  KAFKA_BROKERS: bitnami-kafka.default.svc.cluster.local
  MONGODB_HOST: bitnami-mongodb.default.svc.cluster.local
  MONGODB_PORT: "27017"
  MONGODB_DATABASE: our-spring-boot-app
  MONGODB_USERNAME: root
secretEnvs:
  MONGODB_PASSWORD:
    valueFrom:
      secretKeyRef:
        name: bitnami-mongodb
        key: mongodb-root-password

# Uninstall
helm uninstall our-spring-boot-app
helm uninstall bitnami-mongodb
helm uninstall bitnami-kafka

Set Java version on Ubuntu/Mint

update-alternatives --list java
sudo update-alternatives --install /usr/bin/java java /opt/jdk-14.0.2/bin/java 0
sudo update-alternatives --install /usr/bin/javac javac /opt/jdk-14.0.2/bin/javac 0
sudo update-alternatives --set java /opt/jdk-14.0.2/bin/java
sudo update-alternatives --set javac /opt/jdk-14.0.2/bin/javac

Logging Mockito invocations in Spring

@ExtendWith(SpringExtension::class)
abstract class AbstractSpringUnitTest {

    private val logger = LoggerFactory.getLogger(AbstractSpringUnitTest::class.java)

    @Autowired
    protected lateinit var ctx: ApplicationContext

    @AfterEach
    fun logMockingDetails() {
        ctx.beanDefinitionNames.forEach {
            val bean = ctx.getBean(it)
            if (MockUtil.isMock(bean)) {
                Mockito.mockingDetails(bean)
                    .invocations.forEach { invocation -> logger.info("Mock invocation: $invocation") }
            }
        }
    }
}

Kotlin – type checking Hibernate entities

When using Kotlin and JPA + Hibernate and especially JPA inheritance, you will probably want to type check a polymorphic entity:

val request: RequestEntity = requestRepository.findById(123)
val response: ResponseEntity? = request.response // response is LAZY mapped
return when (response) {
    null -> "pending"
    is ResponseErrorEntity -> "error"
    is ResponseRejectedEntity -> "rejected"
    is ResponseAcceptedEntity -> "accepted"
}

You might be surprised to get following exception:

kotlin.NoWhenBranchMatchedException: null

After all, compiler told us, that this when check is exhaustive.

The reason is, that the real type of response value is something like ResponseAcceptedEntity$HibernateProxy$WS7APdlA

if (response != null) {
    println(response::class)
    println(response is ResponseAcceptedEntity)
}

// package.ResponseAcceptedEntity$HibernateProxy$WS7APdlA
// false

To fix this without changing the mapping from LAZY to EAGER, we use a handy utility function, implemented as a Kotlin extension:

@Suppress("UNCHECKED_CAST")
fun <T> T.unproxy(): T = Hibernate.unproxy(this) as T

We use it explicitly on all lazily mapped entities, than need to be checked by their type:

val response: ResponseEntity? = request.response.unproxy()

Release multi-module Gradle project with Git tags

./gradlew createRelease --no-daemon -x verifyRelease -x test -x ktlint
./gradlew verifyRelease --no-daemon -x test -x ktlint --stacktrace
./gradlew pushRelease --no-daemon -x test -x ktlint --stacktrace
./gradlew artifactoryPublish  --no-daemon -x test --stacktrace

Connect to AWS RDS from your PC

Go to “Configuration”:

Click on the “Endpoint”:

Click on the DB name:

Click on any group:

Select “Inbound” and click “Edit”:

“Add rule”, choose type “MYSQL/Aurora”, Source “MyIP” and “Save”:

Test your connection:

Publishing libraries to local repository using SBT

In my project, I wanted to use ScalaJS wrapper for Google Maps from https://github.com/coreyauger/scalajs-google-maps. It turned out, that library artifacts are not published, where they were supposed to be.

Fortunately, it is very easy to build those artifacts yourself and use them in your project:

  1. Clone the project
  2. Deploy the project to your local repository
  3. Add dependency in your project

Run following commands:

git clone https://github.com/coreyauger/scalajs-google-maps.git
cd scalajs-google-maps
sbt publish local

Add dependency to your ScalaJS project:

libraryDependencies += “io.surfkit” %%% “scalajs-google-maps” % “0.0.3”

Multiple OpenVPN connections on Windows 7

It is possible to connect to multiple networks using OpenVPN for Windows. Here are the steps, that enabled me to connect to 3 VPNs at once on my Windows 7 machine.

This guide was compiled from two StackOverflow answers:

There are limitations to this. Quoting answer by zoredache from the first link:

Obviously you will also need to make sure that nothing about your various VPNs conflict with each other. For example if one is modifying the default gateway you are probably going to have problems. If nothing is changing the default gateway and there are no overlapping IP addresses then you may be ok.

The key is to create multiple TAP adapters, one per every VPN connection.
After you install OpenVPN, you will have one TAP adapter automatically created (I already renamed it, but it was named “Local Area Connection 2” before):

Later, I created two more TAP adapters. After you install OpenVPN, you will also have utilities installed under Start/All Programs/TAP-Windows. Or you can search for it directly using “Add a new TAP”. The script needs to be executed with administrator privileges. Right click on the shortcut (try shift+right click, if it does not work) and “Run as administrator”:

Search for “View network connections”. You will see that a new adapter has been created. You should rename it and give it a simple descriptive name.


I created two additional TAP adapters and named all of them TAPVPN1, TAPVPN2 and TAPVPN3:

The last step is to update your *.ovpn configuration files. For every VPN, add a new setting “dev-node %ADAPTER_NAME%”, e.g.:

...
ns-cert-type server
comp-lzo
verb 3‏
dev-node TAPVPN3
...

Final result:

 

Logging multiline strings

Simple answer – don’t log multiline string. You will loose or benefits coming from tools like grep. The philosophy of these tools is, that every line is a self-contained piece of information. Spreading the information across multiple lines breaks the concept.

So what to do with ‘\n’ or ‘\r’ characters? Replace (escape) them with them literally with “\n” or “\r”. Apply the same to ‘\t’ (tab) and ‘”‘ (double quotes) as well. For example a message like

Response: {
   code: 1,
   message: "No errors found"
}

would by written to a log message like this:

[2015-01-03 16:26:23.136049] ERROR: Response: {\n    code: 1,\n    message: \"No errors found\"\n}

An example of a logging library that enables this behavior is https://github.com/Seldaek/monolog for PHP.

Now it is possible to grep such logs, but they don’t look pretty and are hard to read visually. Here is how to escape back previously escaped characters while tailing multiple logs.

tail -f /var/log/httpd/error_log /var/log/httpd/vhost15/error_log | grep -v " \[info\] " | sed 's/\\r\\n/\n/g; s/\\r/\n/g; s/\\n/\n/g; s/\\t/\t/g; s/\\"/"/g;'

We are using Linux command sed, which is basically a command line text editor. Traditional text editors are interactive, they come with a GUI, where text is displayed, user moves a cursor around and types down the text. Some editors allow to record macros. Macros are small programs and allow to repeat repetitive tasks. sed is a text editor with macros, but without GUI. Because it is a command line utility, it can be combined with other command line utilities, e.g. with tail

In this case, we use sed to recognize simple regular expressions and replace them with other characters. More concretely, we replace escaped Windows EOL (\r\n) with a simple UNIX EOL, escaped Mac EOL with UNIX EOL, escaped UNIX EOL with UNIX EOL itself too and escaped TAB with TAB itself.