# 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()
Testcontainers docker network prune
At certain point, my testcontainers tests stopped working. First, I got the NullPointerException
in the withNetworkMode
method. Later, the could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network error.
For a while I suspected, that this might somehow be linked to a new OpenVPN connection, that I started using.
Anyway, this was solved by docker network prune
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:
- Clone the project
- Deploy the project to your local repository
- 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:
- http://serverfault.com/questions/155299/how-to-connect-multiple-vpns-using-openvpn-on-windows-7-on-the-same-time
- http://stackoverflow.com/questions/21210071/how-to-create-a-2nd-tap-adapter-for-openvpn-client-on-win7
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.