Saturday, June 27, 2026

🧰 Building a Practical Java Utility Library: Exploring common_java

 When working across multiple Java applications, one problem appears again and again:

you keep rewriting the same “basic but essential” components.

common_java is a lightweight utility-style library that groups these repeated concerns into reusable modules — from validation and caching to HTTP, database helpers, and email utilities.

Let’s walk through what this library provides.

📦 1. Project Overview

The library is organized into clear functional packages:

  • validator → input validation logic
  • string → string utilities
  • cache → in-memory caching (including LRU cache)
  • network → HTTP and IP utilities
  • http → URL fetch example app
  • alert → email composition and sending
  • database → JDBC helpers and result handling
  • properties → config loading utilities

This is a classic “common utilities” architecture: each package solves a real-world repetitive backend problem.

✅ 2. Validation Layer

Key classes:

  • Validator
  • EmailValidator

This layer focuses on ensuring data correctness before processing.

Typical responsibilities:

  • checking input format
  • validating email structure
  • centralizing reusable validation rules

💡 Design idea:
Instead of scattering validation logic across services, it is centralized.

Example concept:

Validator.isNotNull(value);

EmailValidator.isValid(email);

🧵 3. String Utilities

Hex.java

This likely provides:

  • hex encoding / decoding
  • conversion between byte arrays and hex strings

💡 Why it matters:
Hex utilities are commonly needed in:

  • encryption
  • networking
  • debugging binary data

⚡ 4. Cache System (Most Interesting Part)

Core classes:

  • Cache
  • LRUCache
  • CacheElement
  • Node
  • LinkedListNode
  • DoublyLinkedList
  • DummyNode
  • CacheApp

🧠 What this module shows

This is a classic LRU Cache implementation using:

  • Hash-based lookup (likely via Cache)
  • Doubly linked list for ordering
  • Node abstraction for entries

🔁 LRU Cache concept

Least Recently Used (LRU) cache works like this:

  • Recently used items stay in memory
  • Old unused items are removed first

🧱 Data structure design

This design typically combines:

  • HashMap → O(1) access
  • Doubly Linked List → O(1) insert/remove

💡 Why multiple node classes?

  • Node → base structure
  • LinkedListNode → real element
  • DummyNode → sentinel node (simplifies edge cases)

This shows clean separation of concerns in data structure design.

🚀 Why this module stands out

This is not just a utility — it’s a fully custom cache engine, useful for:

  • performance optimization
  • memory control
  • backend caching strategies

🌐 5. Network Utilities

Classes:

  • HttpUtil
  • HttpClientApp
  • HttpResponse
  • HttpResponseCode
  • IP

What this module does

It likely provides:

  • HTTP GET/POST helpers
  • response wrapper object
  • HTTP status code abstraction
  • IP-related utilities

💡 Design strength

Instead of directly using low-level HTTP calls everywhere, the library:

✔ wraps HTTP logic
✔ standardizes response handling
✔ centralizes error handling

This improves consistency across projects.

📧 6. Email / Alert System

Classes:

  • Email
  • EmailAddress
  • EmailBody
  • EmailAttachment
  • SMTP
  • EmailApp

What this represents

A structured email system with:

  • strongly typed email components
  • separation of:
    • recipient (EmailAddress)
    • content (EmailBody)
    • attachments (EmailAttachment)
  • SMTP abstraction

💡 Why this design is good

Instead of passing raw strings:

sendEmail(to, subject, body)

You model email as an object:

Email email = new Email(...)

This improves:

  • readability
  • validation
  • maintainability

🗄️ 7. Database Utilities

Classes:

  • DatabaseManager
  • DatabaseStatementManager
  • StatementUtil
  • Jdbc
  • Result
  • RStoListMap

What this module solves

JDBC in Java is powerful but verbose.

This module likely simplifies:

  • connection handling
  • statement execution
  • result set mapping

💡 Key idea: abstraction over JDBC

Instead of repeating boilerplate:

Connection conn = ...
PreparedStatement stmt = ...
ResultSet rs = ...

You centralize it into utilities.

🔁 RStoListMap

This likely converts:

ResultSet → List<Map<String, Object>>

This is extremely useful for:

  • APIs
  • JSON conversion
  • dynamic queries

⚙️ 8. Configuration Utilities

ConfigApp

This likely handles:

  • loading .properties files
  • environment configuration
  • app-level settings

💡 Why it matters:

Every Java system needs config loading, and centralizing it avoids duplication.

🌍 9. HTTP Example App

UrlFetchApp

This is likely a demo or utility showing:

  • fetching URL content
  • using HttpUtil

This acts as:

a usage example of the network module

🧠 Overall Design Observations

✔ Strengths

1. Clear modular separation

Each package has a single responsibility.

2. Real-world utility coverage

It covers:

  • validation
  • caching
  • HTTP
  • email
  • database

This is basically a mini backend toolkit.

3. Strong focus on reusability

Everything is designed to be reused across projects.

🚀 Final Summary

common_java is essentially a personal backend utility framework that helps reduce repetitive Java boilerplate across projects.

It demonstrates:

  • practical backend engineering patterns
  • data structure implementation (LRU cache)
  • abstraction over JDBC and HTTP
  • structured email handling
  • reusable validation logic 
 

Wednesday, June 3, 2026

Run macOS in linux docker container

To be able to run macOS in linux docker container to access macOS applications without switch back and forth makes life easy. This project allow you to do that. The instruction given is very tricky, so I have written a step by step how to begin.


0. This is the main project which we will work on. Essentially we need to build an image where the image is not found in dockerhub at the moment.

```

$ git clone  https://github.com/sickcodes/Docker-OSX

$ cd Docker-OSX

$ docker build -t docker-osx:naked -f Dockerfile.naked .

```


1. Start the macOS installer, this will take some times. If clone repository OpenCorePkg is slow , add this `-v "<USERNAME>/macos/OpenCorePkg:/home/arch/OSX-KVM/OpenCorePkg"` to the command below. But make sure you have clone the repository before https://github.com/acidanthera/opencorepkg

```

$ docker run -it \

    --device /dev/kvm \

    -p 50922:10022 \

    -v /tmp/.X11-unix:/tmp/.X11-unix \

    -e "DISPLAY=${DISPLAY:-:0.0}" \

    -e GENERATE_UNIQUE=true \

    -e MASTER_PLIST_URL='https://raw.githubusercontent.com/sickcodes/osx-serial-generator/master/config-custom.plist' \

    -e SHORTNAME=monterey \

    sickcodes/docker-osx:latest    

```


2. In the mean time, get the container id and note the container id

```

$ docker ps

```


3. A qemu screen will appear, click on `macOS Base System`. wait the messages loading is complete.




4. Once at the installer screen is up, then to erase the biggest disk. You can name the disk any name because it is not important. Example `my_macos_vd`.




5. Then reinstall the macOS. Pick the disk that you erase in step 4. This installation will take a long time. Approximately 1hour in my host machine.

6. Inspect the container and get the directory where we will copy out the disk. 
```
$ docker inspect 0a0fdebac3ac | grep Upper
                "UpperDir": "/var/lib/docker/overlay2/11043fba8b6ed2e4ffeaeb8a439d791f03081cd785a9a14e60024dc3e6c4f461/diff",
```

7. Once installation is done, power down the container. 

8. Based on the step 6, the value of `UpperDir`, now copy the `mac_hdd_ng.img` to another directory in your host. Any directory will do. I pick `~/macos/`
```
$ sudo su -
# cd /var/lib/docker/overlay2/11043fba8b6ed2e4ffeaeb8a439d791f03081cd785a9a14e60024dc3e6c4f461/diff
# ls -lh ./home/arch/OSX-KVM/mac_hdd_ng.img
-rw-r--r-- 1 jason jason 37G Jun  2 20:43 ./home/arch/OSX-KVM/mac_hdd_ng.img
# cp ./home/arch/OSX-KVM/mac_hdd_ng.img </home/USERNAME/>/macos/
```

9. Update the file permission to your username correctly. 
```
$ cd ~/macos/
$ sudo chown jason:jason mac_hdd_ng.img
$ chmod 664 mac_hdd_ng.img
```

10. Now start the container using the virtual disk, the local built naked image to finish the OS setup/configuration. It will takes more than 30 minutes to complete at least.

```
docker run -it \
    --device /dev/kvm \
    -p 50922:10022 \
    -v "/home/jason/macos/mac_hdd_ng.img:/image" \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    -e "DISPLAY=${DISPLAY:-:0.0}" \
    -e MASTER_PLIST_URL=https://raw.githubusercontent.com/sickcodes/Docker-OSX/master/custom/config-nopicker-custom.plist \
    -e SHORTNAME=monterey \
    docker-osx:naked
```  
to speed up the container, add the below parameters to the command above if your host hardware support it.
```
    -m 10g \
    --cpus="8.0" \
    --gpus all \
    --device /dev/nvidia0 \
    --device /dev/nvidia-uvm \
    --device /dev/nvidia-uvm-tools \
    --device /dev/nvidiactl \
```

11. Once started, get the container id again. After the mac os setup/configuration is finish, then stop the container. Note that during this step, OS reboot is expected.

```
$ docker ps
```




12. Now every time to start the container
```
$ docker start <container id from step 11>
```




It is a pity that the macOS after Monterey does not work. If you managed to make it work, I would like to know and please contact me!

Saturday, March 28, 2026

🇲🇾 A Simple, Fast EPF Calculator for Malaysians — Built for Everyday Use

 

🇲🇾 A Simple, Fast EPF Calculator for Malaysians — Built for Everyday Use

Managing your finances in Malaysia often means keeping track of your EPF (Employees Provident Fund) contributions — whether you’re an employee, employer, or freelancer planning ahead.

That’s exactly why I built this lightweight, no-frills web app:

👉 https://weetech-software.github.io/epf/


💡 Why This EPF Calculator Exists

Most EPF calculators online tend to be:

  • Overly complex

  • Slow to load

  • Filled with ads or unnecessary steps

I wanted something different:

  • Fast

  • 🧼 Clean UI

  • 🎯 Straight to the point

This tool is designed to help you quickly answer questions like:

  • How much EPF will I contribute this month?

  • What is my employer contributing?

  • What’s the total deduction from my salary?


🧮 What the Calculator Does

The calculator lets you:

  • Input your monthly salary

  • Instantly see:

    • Employee EPF contribution

    • Employer EPF contribution

    • Total EPF contribution

It reflects standard EPF contribution structures used in Malaysia, making it practical for:

  • Employees checking payslips

  • HR or payroll quick checks

  • Anyone planning savings projections


🚀 Key Features

⚡ Instant Calculation

No loading screens, no waiting — results update immediately as you type.

🧠 Simple & Intuitive

No financial jargon. Just enter your salary and get your numbers.

📱 Works Everywhere

Fully web-based, so you can use it on:

  • Desktop

  • Mobile

  • Tablet

No installation needed.

🔒 Privacy-Friendly

  • No login

  • No tracking

  • No data stored

Everything runs directly in your browser.


🛠️ Built for Practical Use

This isn’t meant to replace official tools — it’s meant to make your daily life easier.

Whether you’re:

  • Checking your take-home salary

  • Estimating job offers

  • Planning monthly finances

This tool gives you quick, reliable answers without friction.


📌 Example Use Case

Let’s say you’re offered a salary of RM5,000.

Instead of manually calculating percentages or digging through EPF tables, you can:

  1. Open the calculator

  2. Enter 5000

  3. Instantly see:

    • Your contribution

    • Employer contribution

    • Total EPF savings

Done in seconds.


🌱 Future Improvements

Some ideas I’m considering:

  • Support for different EPF contribution rates (e.g. reduced rates)

  • Yearly projection / savings growth

  • Printable summary

  • More financial tools in one place


🙌 Try It Out

If you’re in Malaysia and want a quick way to calculate EPF:

👉 https://weetech-software.github.io/epf/


💬 Feedback Welcome

If you find it useful or have ideas to improve it, feel free to share feedback. The goal is to keep it simple, fast, and genuinely helpful.


Built with simplicity in mind — because financial tools shouldn’t be complicated.

Saturday, March 14, 2026

Integrate alert_threshold into Jenkins

To run this application periodically and ensure system admins/support receive timely alerts, a Jenkins installation is required.

  1. Make sure you have already run this app as described in the How to run this app section.
  2. Download and install Jenkins. While Jenkins setup is outside the scope of this document, more information can be found here.
  3. Create a Freestyle Project in Jenkins and apply the appropriate settings. The minimum required settings are:
    • Build periodically
    • Execute Shell
  4. That’s it! Check the console output for any errors. A YouTube video has also been created to briefly demonstrate this integration:


Sunday, October 12, 2025

How to draw arrow in GIMP using plugin

GIMP is a great image editing program that allows us to alter images and better convey our intentions. I like using GIMP for tasks such as resizing, changing formats, or cropping images. In this article, I will explain how to draw an arrow in GIMP.

First, download the arrow.zip which contains two Scheme script files. There should be 3 files in total. Below is a summary of the files:

  • ArrowScriptNotes.pdf : description of the arrow.
  • arrow.scm            : Scheme script file for gimp version 2.x
  • arrow_V3.scm         : Scheme script file for gimp version 3.x
Depending on your GIMP installation, copy the appropriate file to the GIMP script directory and then make it executable. For example , I'm using GIMP version 3.0.4-3 so I would do the following:

$ cp arrow_V3.scm $HOME/.config/GIMP/3.0/scripts
$ chmod 770 $HOME/.config/GIMP/3.0/scripts/arrow_V3.scm
$ # for GIMP version 2.x
$ # cp arrow_V3.scm $HOME/.config/GIMP/2.10/scripts

Restart GIMP if it is already running. I also created a YouTube video demonstrating how to draw an arrow using this plugin.




Monday, November 11, 2024

APC UPS 1200VA connected to linux debian

On 1st November 2023 was one of the worst ever day in my working life. One super strongest ever lightning strike completely shut off the the whole house electric supply! I have UPS connected to my laptop which the laptop also shut off abruptly. I would say "lucky" ? that that the laptop managed to boot up unfortunately the USBs are all broken, it does not function anymore. Never in my life would think that even with a laptop protected by an APC UPS, the device would be damage, well partially. Closer inspection show that laptop power adapter socket, the live connector got burned mark. It is just how strong was the lightning strike! So lesson learn, this time I bought a new APC UPS which connected in serial fashion and behind a surge protector (Targus) and hoping that with 2x protections, I will survive in the incoming lightning storm. hopefully. 🤞🙏

Let's see the APC UPS






and when it is connected and running



This unit come with monitoring system. It can be connected to the pc device via USB cable which is awesome! Best part is, if you are using linux, it comes detected automagically. At the time of this writing, I'm using Debian 12.7. 

what you need to do is just installed package apcupsd

$ sudo apt install apcupsd
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following packages were automatically installed and are no longer required:
  hunspell-gl-es hunspell-sv-se libnvidia-compiler libu2f-udev libwpe-1.0-1 libwpebackend-fdo-1.0-1 linux-headers-6.1.0-17-common
Use 'sudo apt autoremove' to remove them.
The following additional packages will be installed:
  apcupsd-doc libusb-0.1-4
Suggested packages:
  apcupsd-cgi
The following NEW packages will be installed:
  apcupsd apcupsd-doc libusb-0.1-4
0 upgraded, 3 newly installed, 0 to remove and 0 not upgraded.
Need to get 670 kB of archives.
After this operation, 1,779 kB of additional disk space will be used.
Do you want to continue? [Y/n] Y
Get:1 http://deb.debian.org/debian bookworm/main amd64 libusb-0.1-4 amd64 2:0.1.12-32 [23.4 kB]
Get:2 http://deb.debian.org/debian bookworm/main amd64 apcupsd amd64 3.14.14-3.1+b1 [256 kB]
Get:3 http://deb.debian.org/debian bookworm/main amd64 apcupsd-doc all 3.14.14-3.1 [390 kB]
Fetched 670 kB in 0s (3,160 kB/s)    
Selecting previously unselected package libusb-0.1-4:amd64.
(Reading database ... 438864 files and directories currently installed.)
Preparing to unpack .../libusb-0.1-4_2%3a0.1.12-32_amd64.deb ...
Unpacking libusb-0.1-4:amd64 (2:0.1.12-32) ...
Selecting previously unselected package apcupsd.
Preparing to unpack .../apcupsd_3.14.14-3.1+b1_amd64.deb ...
Unpacking apcupsd (3.14.14-3.1+b1) ...
Selecting previously unselected package apcupsd-doc.
Preparing to unpack .../apcupsd-doc_3.14.14-3.1_all.deb ...
Unpacking apcupsd-doc (3.14.14-3.1) ...
Setting up libusb-0.1-4:amd64 (2:0.1.12-32) ...
Setting up apcupsd-doc (3.14.14-3.1) ...
Setting up apcupsd (3.14.14-3.1+b1) ...
Created symlink /etc/systemd/system/multi-user.target.wants/apcupsd.service → /lib/systemd/system/apcupsd.service.
Processing triggers for man-db (2.11.2-2) ...
Processing triggers for libc-bin (2.36-9+deb12u7) ...
Scanning processes...                                                                                                                                                                         
Scanning processor microcode...                                                                                                                                                               
Scanning linux images...                                                                                                                                                                      

Running kernel seems to be up-to-date.

The processor microcode seems to be up-to-date.

No services need to be restarted.

No containers need to be restarted.

No user sessions are running outdated binaries.

No VM guests are running outdated hypervisor (qemu) binaries on this host.

and then start it.

$ sudo systemctl start apcupsd
$ sudo systemctl status apcupsd
● apcupsd.service - UPS power management daemon
     Loaded: loaded (/lib/systemd/system/apcupsd.service; enabled; preset: enabled)
     Active: active (running) since Mon 2024-11-11 21:32:23 +08; 2s ago
       Docs: man:apcupsd(8)
    Process: 212186 ExecStartPre=/lib/apcupsd/prestart (code=exited, status=0/SUCCESS)
    Process: 212190 ExecStart=/sbin/apcupsd (code=exited, status=0/SUCCESS)
   Main PID: 212191 (apcupsd)
      Tasks: 3 (limit: 37451)
     Memory: 584.0K
        CPU: 8ms
     CGroup: /system.slice/apcupsd.service
             └─212191 /sbin/apcupsd

Nov 11 21:32:23 debby systemd[1]: Starting apcupsd.service - UPS power management daemon...
Nov 11 21:32:23 debby systemd[1]: apcupsd.service: Can't open PID file /run/apcupsd.pid (yet?) after start: No such file or directory
Nov 11 21:32:23 debby apcupsd[212191]: apcupsd 3.14.14 (31 May 2016) debian startup succeeded
Nov 11 21:32:23 debby apcupsd[212191]: NIS server startup succeeded
Nov 11 21:32:23 debby systemd[1]: Started apcupsd.service - UPS power management daemon.

and even better, gnome automatically detect it. I was really impress how little effort require to make things work.



one thing it does not work out of the box is communication with UPS also lost... but not sure how gnome able to detect the battery percentage..anyway, guess this is the work require.

$ sudo apcaccess

APC      : 001,018,0437

DATE     : 2024-11-11 21:33:20 +0800  

HOSTNAME : debby

VERSION  : 3.14.14 (31 May 2016) debian

UPSNAME  : debby

CABLE    : USB Cable

DRIVER   : USB UPS Driver

UPSMODE  : Stand Alone

STARTTIME: 2024-11-11 21:32:23 +0800  

STATUS   : COMMLOST 

MBATTCHG : 5 Percent

MINTIMEL : 3 Minutes

MAXTIME  : 0 Seconds

NUMXFERS : 0

TONBATT  : 0 Seconds

CUMONBATT: 0 Seconds

XOFFBATT : N/A

STATFLAG : 0x05000100

END APC  : 2024-11-11 21:33:20 +0800  

In any case, I really like this unit come with monitoring system. I guess just need to find out how to fix the communication problem and it would be a blast!


Reference:

https://opensource.com/article/21/12/linux-apcupsd

https://wiki.debian.org/apcupsd

 

Sunday, June 2, 2024

my code is your code

 If tomorrow is debug day (Type "function")

And I'm standin' with the front-end design

And the lead asks me what I did with my code

I will say I coded with you


If I wake up in Code War III (Code War III)

I see bugs and vulnerabilities

And I feel like I want to go home

It's okay if you're debugging with me


(

Cuz your code is my code

My code is your code

It would take an infinity loop to break us

And the chains of GitHub couldn't fork us

) x2


If I lose my fame and followers (Really don't matter)

And my server crashes down (Really don't matter)

As long as I got your commits (Really don't matter)

It really don't matter (Really don't matter)


As the versions they pass us by (the versions, the versions, the versions, the versions, the versions)

We stay updated through each other's code (each other's code)

And no matter how many updates we make

It's okay as long as I got your merge



(

Cuz your code is my code

My code is your code

It would take an infinity loop to break us

And the chains of GitHub couldn't fork us

) x2


If my code should break today (Break today)

Don't worry, 'cause in code we iterate anyway (Oh no)

"No matter the chatter from others (truly inconsequential)

I'll stand by, awaiting you beyond the reckoning's sway"


(

Cuz your code is my code

My code is your code

It would take an infinity loop to break us

And the chains of GitHub couldn't fork us

) x2

Saturday, May 20, 2023

Now I know why zoom feature is available in operating system for.

I use to think why would an O/S feature is created as it sudden zoom ridiculous 10-100x of the current resolution. 

But as one age, it FINALLY make sense, all comes around! Let's give an example. Given the current view at current resolution of the following picture

Given the following webshot, what's the difference can you identify?



Sure, some with very good eye can spot but it would also take sometime to identify. But if you can zoom in, wouldn't that be nice?! In linux , try super + alt + 8, yes the 8 is not on the numpad. Now it should be clearly shown what is the difference.


The display might be too small, try open the image in a new window tab. It should be clearly seen that the vertical line is dashed whilst the horizontal line is dotted. Well that's the difference!

It is like the zoom feature allow one to inspect the DNA.. well, computer DNA.. :) 


Tuesday, March 7, 2023

how to setup mariadb galera cluster in docker container

 1. create network (one time)

   docker network create --subnet=172.18.0.0/16 mynet123


2. create image that can start service in container (one time)

   mkdir systemd

   cd systemd

   vim Dockerfile

   FROM almalinux

   ENV container docker


   RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in ; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done);


   RUN rm -rf /lib/systemd/system/multi-user.target.wants/ \

   && rm -rf /etc/systemd/system/.wants/ \

   && rm -rf /lib/systemd/system/local-fs.target.wants/ \

   && rm -f /lib/systemd/system/sockets.target.wants/udev \

   && rm -f /lib/systemd/system/sockets.target.wants/initctl \

   && rm -rf /lib/systemd/system/basic.target.wants/ \

   && rm -f /lib/systemd/system/anaconda.target.wants/*


   VOLUME [ “/sys/fs/cgroup” ]

   CMD ["/usr/sbin/init"]


3. build image (one time)

   docker build -t almalinux-md .


4. start the container in the background

   docker run -v /tmp/node1/:/var/lib/mysql --detach --rm -it --privileged --net mynet123 --ip 172.18.0.11 --name node1  almalinux-md 

   docker run -v /tmp/node2/:/var/lib/mysql --detach --rm -it --privileged --net mynet123 --ip 172.18.0.12 --name node2  almalinux-md

   docker run -v /tmp/node3/:/var/lib/mysql --detach --rm -it --privileged --net mynet123 --ip 172.18.0.13 --name node3  almalinux-md  


5. attach it and so can install mariadb and gelera, etc

   docker exec -it node1 bash

   docker exec -it node2 bash

   docker exec -it node3 bash


6. install in all 3 nodes

   dnf install -y mariadb-server.x86_64  mariadb.x86_64 rsync python3-policycoreutils vim nc telnet epel-release.noarch mariadb-server-galera.x86_64


7. on first node

   systemctl start mariadb

   systemctl status mariadb

   [root@6c3d7f5bc394 /]# mysql -uroot

   MariaDB [(none)]> set password = password("password");

   quit;

   systemctl stop mariadb

      

8. from step 5. wsrep_node_address and wsrep_node_name update to its corresponding name   


   [root@cfeee129fe92 /]# cat /etc/my.cnf.d/galera.cnf

   [mysqld]

   binlog_format=ROW

   default-storage-engine=innodb

   innodb_autoinc_lock_mode=2

   bind-address=0.0.0.0


   # Galera Provider Configuration

   wsrep_on=ON

   wsrep_provider=/usr/lib64/galera/libgalera_smm.so


   # Galera Cluster Configuration

   wsrep_cluster_name="ot_mariadb_cluster"

   wsrep_cluster_address="gcomm://172.18.0.11,172.18.0.12,172.18.0.13"


   # Galera Synchronization Configuration

   wsrep_sst_method=rsync


   # Galera Node Configuration

   wsrep_node_address="172.18.0.13"

   wsrep_node_name="node3"

   [root@cfeee129fe92 /]# 


9.  on first node, start the cluster

    galera_new_cluster

   

10. check on the first node,

    mysql -u root -ppassword -e "SHOW STATUS LIKE 'wsrep_cluster_size'"   

   

11. bring up the remaining nodes one by one

    systemctl start mariadb

    mysql -u root -ppassword -e "SHOW STATUS LIKE 'wsrep_cluster_size'"   


12. ready to write/read

mysql -u root -ppassword -e 'CREATE DATABASE playground;

CREATE TABLE playground.equipment ( id INT NOT NULL AUTO_INCREMENT, type VARCHAR(50), quant INT, color VARCHAR(25), PRIMARY KEY(id));

INSERT INTO playground.equipment (type, quant, color) VALUES ("slide", 2, "blue");'


mysql -u root -ppassword -e 'SELECT * FROM playground.equipment;'

Thursday, February 24, 2022

Best place I think docker container is for

 docker container has been with us for many years. For software engineer which deal with legacy application, it can be intimidating to migrate to docker application. Because system stability, production investigation and quick fix and service stability surpasses every technology stack. At least that is for me, this cannot be compromise. 


So where is docker container is best for? Where do we start docker container for? For many people, if you google, they build the application into docker image and deploy. Sure, that can be done but for me, as a start, I think for learning purposes and best use cases. Here, I would like share my experience and best use cases.


code compiling

Many of us do code compiling and this is especially true if you want to deploy a bunch of application, modify here and there, build the binary , get the final result and then move on. Installing software packages into container will not taint the host. Modify the configuration also need not worry about taint the host. What is even more merrier, you don't have to clean up after you done compiling. One can just delete the container and start over. Guaranteed reproducible end result. This is similar to chroot but container allow you to do further with different o/s.


application on legacy database

sometime when we migrate development server to the latest o/s but however it break the legacy application or its dependency, example database. In such a case, docker can come to the rescue, you can install legacy database in the docker container whilst the application still can access the database without any puzzle or not expected result. This is even more true when you are busy and focus on upgrading the development server, you don't have the time to upgrade the application or its database. When you are done develop development server, you can then upgrade the database to another version inside docker. I think docker in this case is a really good fit.


quick test for code

Many times when you write bash script, you want to quickly test if the script works fine. But you don't want to test run on your machine as it will change the underlying configuration/state. If you run a container, those code can be quickly test it and when you are done, you can exit the container and nothing harmful done to the host. In this case, you are totally sure your code is tested well and in the same time with less maintenance after tests.


That's it I want to share my opinions and use cases for docker container. If I learn new way of using docker, I will update this document. I hope you will find it useful for your use cases.



Friday, June 25, 2021

How to cherry pick on a commit but only a few files from upstream patch

I forked a repository from upstream and there was a patch I like and I would like that patch goes into my forked repository. But at the same time, I would like to pick only a few changes, that is, not the whole commit and at the same time too, I need to make some minor modifications. So here goes how this is done.

Let's check where we are at now.

 jason@localhost:~/advanced-policy-firewall$ git branch  
 * master  
  rfxn_upstream  
  robertoberto-patch  
  support-ipv6  

Get the commit id from roberto patch, cherry pick the whole commit but do not commit yet.. just stage at the current branch (which is master)

 jason@localhost:~/advanced-policy-firewall$ git cherry-pick -n 0d1df6549820f9592aefb2353b77c52eadbe759f  
 Auto-merging files/vnet/vnetgen  
 CONFLICT (content): Merge conflict in files/vnet/vnetgen  
 error: could not apply 0d1df65... - fixed vnetgen for newer linux distros to prioritize ip over ipconfig  
 hint: after resolving the conflicts, mark the corrected paths  
 hint: with 'git add <paths>' or 'git rm <paths>'  
 jason@localhost:~/advanced-policy-firewall$ git status .  
 On branch master  
 Your branch is up to date with 'origin/master'.  
 Changes to be committed:  
  (use "git restore --staged <file>..." to unstage)  
      modified:  README  
      new file:  test/distros_examples/ifconfig_ubuntu_12  
      new file:  test/distros_examples/ifconfig_ubuntu_20  
      new file:  test/distros_examples/ip_addr_ubuntu_12  
      new file:  test/distros_examples/ip_addr_ubuntu_20  
      new file:  test/distros_examples/ip_link_ubuntu_12  
      new file:  test/distros_examples/ip_link_ubuntu_20  
 Unmerged paths:  
  (use "git restore --staged <file>..." to unstage)  
  (use "git add <file>..." to mark resolution)  
      both modified:  files/vnet/vnetgen  

Let's get the diff of the stage file

 jason@localhost:~/advanced-policy-firewall$ git diff --cached README  
 diff --git a/README b/README  
 index 07ad004..0adf39d 100644  
 --- a/README  
 +++ b/README  
 @@ -199,7 +199,7 @@ Fedora Core Any  
  Slackware 8.0+  
  Debian GNU/Linux 3.0+  
  Suse Linux 8.1+  
 -Unbuntu Any  
 +Ubuntu Any  
  TurboLinux Server 9+  
  TurboLinux Fuji (Desktop)  
  RedHat Linux 7.3,8,9  

Unstage a file because I wanna make minor modification. After changes are make, add it back to the stage

 $ git restore --staged test/distros_examples/ip_link_ubuntu_12  
 $ vim test/distros_examples/ip_link_ubuntu_12  
 $ git add test/distros_examples/ip_link_ubuntu_12  

All good now, let's commit

 $ git commit -m "cherry pick but only selected files from roberto patched"  
 [master 80830c9] cherry pick but only selected files from roberto patched  
  8 files changed, 330 insertions(+), 28 deletions(-)  
  create mode 100644 test/distros_examples/ifconfig_ubuntu_12  
  create mode 100644 test/distros_examples/ifconfig_ubuntu_20  
  create mode 100644 test/distros_examples/ip_addr_ubuntu_12  
  create mode 100644 test/distros_examples/ip_addr_ubuntu_20  
  create mode 100644 test/distros_examples/ip_link_ubuntu_12  
  create mode 100644 test/distros_examples/ip_link_ubuntu_20  
 $ git branch  
 * master  
  rfxn_upstream  
  robertoberto-patch  
  support-ipv6  

and we push the changes to github

 $ git push  
 Enumerating objects: 19, done.  
 Counting objects: 100% (19/19), done.  
 Delta compression using up to 16 threads  
 Compressing objects: 100% (13/13), done.  
 Writing objects: 100% (14/14), 3.68 KiB | 3.68 MiB/s, done.  
 Total 14 (delta 7), reused 0 (delta 0), pack-reused 0  
 remote: Resolving deltas: 100% (7/7), completed with 4 local objects.  
 To https://github.com/jasonwee/advanced-policy-firewall.git  
   f98e9bb..80830c9 master -> master  

in case you want to see the final result, please visit this github commit.

Monday, June 21, 2021

Continuous Integration with GitHub Action

This is a contribution to the SUSE Cloud Native Foundations Scholarship Program which I received and some of the peers want a CI online demo. So here I will share on my knowledge and experience on continuous integration using a public repository and public runner. I will start with a sample project creation, GitHub project setup and end with GitHub action setup and runner. 


What is CI? 

In software engineering, continuous integration (CI) is the practice of merging  all developers' working copies to a shared mainline several times a day.[1]


So essentially, it is a routine where every developer has to go through after they made code changes. Example, syntax check, linting, code compiling, multiple tests, package building, perhaps also support different runtime versions and/or operating systems.

There are many CI software available, please choose the one which match your requirements. I particularly like Jenkins a lot but Jenkins would require you to setup the runner on your machine. Since the objective of this blog is all public, hence, I picked GitHub action.

Let's begin with initial project setup. Reference if you want to know more.

https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html

https://docs.github.com/en/actions/guides/building-and-testing-java-with-maven


```

$ export JAVA_HOME=/usr/lib/jvm/jdk-11.0.5/

$ mvn --version

Apache Maven 3.6.3

Maven home: /usr/share/maven

Java version: 11.0.5, vendor: Oracle Corporation, runtime: /usr/lib/jvm/jdk-11.0.5

Default locale: en_US, platform encoding: UTF-8

OS name: "linux", version: "5.10.0-7-amd64", arch: "amd64", family: "unix"

$ mvn archetype:generate -DgroupId=ch.weetech.app -DartifactId=demo_ci -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false

[INFO] Scanning for projects...

[INFO] 

[INFO] ------------------< org.apache.maven:standalone-pom >-------------------

[INFO] Building Maven Stub Project (No POM) 1

[INFO] --------------------------------[ pom ]---------------------------------

...

...

[INFO] ------------------------------------------------------------------------

[INFO] BUILD SUCCESS

[INFO] ------------------------------------------------------------------------

[INFO] Total time:  5.555 s

[INFO] Finished at: 2021-06-20T14:53:50+08:00

[INFO] ------------------------------------------------------------------------

$ cd demo_ci

$ tree .

.

├── pom.xml

└── src

    ├── main

    │   └── java

    │       └── ch

    │           └── weetech

    │               └── app

    │                   └── App.java

    └── test

        └── java

            └── ch

                └── weetech

                    └── app

                        └── AppTest.java


11 directories, 3 files

$ mvn package


[INFO] Scanning for projects...

[INFO] 

[INFO] -----------------------< ch.weetech.app:demo_ci >-----------------------

[INFO] Building demo_ci 1.0-SNAPSHOT

[INFO] --------------------------------[ jar ]---------------------------------

[INFO] 

[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ demo_ci ---

[INFO] Using 'UTF-8' encoding to copy filtered resources.

...

...

...

[INFO] ------------------------------------------------------------------------

[INFO] BUILD SUCCESS

[INFO] ------------------------------------------------------------------------

[INFO] Total time:  2.221 s

[INFO] Finished at: 2021-06-20T14:56:41+08:00

[INFO] ------------------------------------------------------------------------

$ java -cp target/demo_ci-1.0-SNAPSHOT.jar ch.weetech.app.App

Hello World!

```

Once we have the sample project setup, test and compile code locally okay. Then it is time that we initialize git repository locally and push to GitHub. But before  we do of the following, you need to create a new repository on your GitHub.


```

$ mvn clean

[INFO] Scanning for projects...

[INFO] 

[INFO] -----------------------< ch.weetech.app:demo_ci >-----------------------

[INFO] Building demo_ci 1.0-SNAPSHOT

[INFO] --------------------------------[ jar ]---------------------------------

[INFO] ------------------------------------------------------------------------

[INFO] BUILD SUCCESS

[INFO] ------------------------------------------------------------------------

[INFO] Total time:  2.079 s

[INFO] Finished at: 2021-06-20T15:24:46+08:00

[INFO] ------------------------------------------------------------------------

$ git init 

hint: Using 'master' as the name for the initial branch. This default branch name

hint: is subject to change. To configure the initial branch name to use in all

hint: of your new repositories, which will suppress this warning, call:

hint: 

hint: git config --global init.defaultBranch <name>

hint: 

hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and

hint: 'development'. The just-created branch can be renamed via this command:

hint: 

hint: git branch -m <name>

$ git status -m master

$ git status .

On branch master


No commits yet


Untracked files:

  (use "git add <file>..." to include in what will be committed)

pom.xml

src/


nothing added to commit but untracked files present (use "git add" to track)

$ git add pom.xml src/

$ git commit -m "initial"

[master (root-commit) 2cd8641] initial

 3 files changed, 108 insertions(+)

 create mode 100644 pom.xml

 create mode 100644 src/main/java/ch/weetech/app/App.java

 create mode 100644 src/test/java/ch/weetech/app/AppTest.java

$ git remote add origin https://github.com/jasonwee/demo_ci.git

$ git branch -M main

$ git push -u origin main

Enumerating objects: 16, done.

Counting objects: 100% (16/16), done.

Delta compression using up to 16 threads

Compressing objects: 100% (6/6), done.

Writing objects: 100% (16/16), 1.76 KiB | 899.00 KiB/s, done.

Total 16 (delta 0), reused 0 (delta 0), pack-reused 0

To https://github.com/jasonwee/demo_ci.git

 * [new branch]      main -> main

Branch 'main' set up to track remote branch 'main' from 'origin'.

$ git status .

On branch main

Your branch is up to date with 'origin/main'.


nothing to commit, working tree clean


```

Now that you pushed everything to GitHub, next is to setup GitHub Actions.

1. Locate Actions from the project tab.

2. Picked 'Java with Maven' as this best match the current requirement.

3. Check action configurations are okay and commit it. 



4. once you commit, the action will kickstart the first run.


Congratulation! This is the first CI that you setup. The rest, as you may have already guessed will be the same as every developer will do, i.e. making code changes.

```

$ vim README.md

$ cat README.md

a contribution to https://www.udacity.com/scholarships/suse-cloud-native-foundations-scholarship

$ git status .

On branch main

Your branch is up to date with 'origin/main'.


Untracked files:

  (use "git add <file>..." to include in what will be committed)

README.md


nothing added to commit but untracked files present (use "git add" to track)

$ git add README.md 

$ git commit -m "added readme" README.md 

[main a966f09] added readme

 1 file changed, 1 insertion(+)

 create mode 100644 README.md

$ git push

Enumerating objects: 4, done.

Counting objects: 100% (4/4), done.

Delta compression using up to 16 threads

Compressing objects: 100% (3/3), done.

Writing objects: 100% (3/3), 410 bytes | 410.00 KiB/s, done.

Total 3 (delta 0), reused 0 (delta 0), pack-reused 0

To https://github.com/jasonwee/demo_ci.git

   a273126..a966f09  main -> main

```





That's it, in case you figure where can I find this repository to begin mine? https://github.com/jasonwee/demo_ci 


Friday, December 18, 2020

OpenHAB vs Xiaomi Sensor

original from https://www.planetknauer.net/blog/archives/2020-12-OpenHAB-vs-Xiaomi-Sensoren.html 


Some time ago I started to use OpenHAB to expand the monitoring of my servers to the whole apartment. It would be nice to know whether I closed the freezer in the basement again or if I left the door open out of sheer senility. Corresponding sensors are available for relatively little money, e.g. from Xiaomi (or from other manufacturers for a lot more money, whether they are easier to integrate is the big question). However, the integration of the Xiaomi sensors in OpenHAB is a Chinese opera in several acts: 

 Act 1: The starter set The starter set is available for around 80.- and it comes with a pressure switch, two door sensors and two motion detectors as well as the base station. According to the description, everything is very simple: Start the base station, install the Mihome app on an Android phone, select "Mainland China" as the server location, connect the app to the base station, activate developer mode via a hidden menu, read out the key and so that the base station can be integrated into OpenHAB. Then everything would be very easy with a data flow sensor -> Xiaomi Hub -> OpenHAB. Yes but... Exactly, because the whole thing only works with the "Mainland China Edition" base station, which is not available in Europe. You can presumably have them delivered overseas through a Chinese wholesaler (don't forget the travel adapter, Chinese plugs don't fit into European sockets). But the EU edition is unusable: - If you select "Mainland China" as the server location in the app, the base station cannot be found and therefore cannot be connected - If you choose the server location "Europe", the hidden menu to activate the developer mode is missing. 

 Act 2: Obsolete app With a little research it turned out that an outdated version of the Mihome app can be found on dubious pages, which contains an error: It writes a debug log. Where to find the access key. Unfortunately, that alone doesn't help. This allows you to integrate the hub via the Xiaomi Mi IO extension from OpenHAB. But that was all. As a result, there is still no access to the sensors. For this, the developer mode would still have to be activated, which also opens Telnet access on the device. Now there are two more options: A modified Mihome app from a Russian website that is completely in Cyrillic. Well uh ... Njet! Or unpack the soldering iron, tap the serial port, use it to gain terminal access and activate the Telnet server. Now that there is a good chance that this can destroy the device, I prefer to leave it (for now). At least I could resell it while it still works. 

 Act 3: The Aqara Hub A further look at the documentation of the Mihome binding shows: The hub in version 3 (which is available as Aqara Hub for Apple Homekit) should be a little more accessible. Unfortunately, that costs almost as much as the whole set. And then just as little can. I immediately sent it back accordingly ...

Act 4: Cheap Zigbee Stick
The Xiaomi devices, like all proprietary garbage, of course never exactly adhere to the standards. But at least enough that the protocol just barely passes as Zigbee. So I bought a USB Zigbee stick from the richest man in the world for € 9 + € 3.50 shipping. To my great surprise, this, although electronic device, it was sent from amazon germany to switzerland. Very unusual. And arrived super fast too. Also unusual.

It is a simple USB stick with a CC2531 chip and zigbee2mqtt-compatible firmware preinstalled. Very awesome!

In principle, OpenHAB would be able to address the Zigbee stick directly via the Zigbee binding. The data flow would then be Sensor -> USB stick -> OpenHAB. But there was something with the protocol standard at Xiaomi and stick to it. The sensors can be integrated, but are displayed as "offline" and no status can be queried. As usual, the following applies: why easy when it can be complicated?

Now the from-behind-through-the-chest-in-the-eye installation for the data flow sensor -> USB stick -> zigbee2mqtt -> mqtt-broker -> OpenHAB begins.
First the stick is connected, it is recognized as a USB serial device / dev / ttyACM0.
Now an MQTT broker has to be installed, e.g. mosquitto from the Debian package sources. This is started without further configuration.
Next, zigbee2mqtt is installed with what feels like two thousand Node.JS dependencies (including npm from the Debian backports if you use Debian Stable as a base). In contrast to the OpenHAB part that follows later, this is excellently documented, so that this part feels more like paint-by-numbers than system administration.

In principle, the devices can now be integrated. Simply reset the sensor with the SIM pin included in the package, and that's it. According to the instructions, you may have to repeat the process several times, but with the first two sensors it worked right away. A look at journalctl -u zigbee2mqtt -f shows activity.

Now comes the hard part: connecting OpenHAB to MQTT. This is documented very superficially and abstractly. Add to this the chaos with instructions for MQTT1 and MQTT2 binding when you google for solutions. Which now applies to my installation? Boh? Ultimately, I followed the instructions for MQTT2, and that worked at some point. Probably: MQTT1 == OpenHAB1, MQTT2 == OpenHAB2 (and I'm running 2.5).

How to proceed:
In the zigbee2mqtt configuration file /opt/zigbee2mqtt/data/configuration.yaml, the output should not be output as JSON but as an attribute. To do this, insert the following lines, save, restart zigbee2mqtt:

experimental:
    output: attribute

And if we are already fiddling with the configuration, one should also assign sensible friendly_name to the sensors.
First install the MQTT binding in OpenHAB.
Then create a .things file with the required entries in /etc/openhab2/things /. At some point I found halfway suitable instructions in the forum ...
And now you are surprised that the things appear in the GUI, but no data is read ... Signal strength? NaN. Battery level? NaN. Status? Off. grrrmpf. After a long debugging process (yes, zigbee2mqtt writes in mosquitto, you can read along with mosquitto_sub -v -t '#') at some point just triggered the spontaneous Windows reflex and restarted OpenHAB itself. Aaand! Bingo! Everything works. So easy! Incidentally, the restart is necessary for every newly added (or renamed) device.


The finale: the OpenHAB Things file


Bridge mqtt:broker:MosquittoMqttBroker "Mosquitto MQTT Broker" [ host="127.0.0.1", secure=false] {
Thing topic xdoor1 "Xiaomi Door Sensor" @ "Location" {
Channels:
Type switch : contact "contact" [ stateTopic = "zigbee2mqtt/xdoor1/contact", on="true", off="false" ]
Type number : voltage "voltage" [ stateTopic = "zigbee2mqtt/xdoor1/voltage" ]
Type number : battery "battery" [ stateTopic = "zigbee2mqtt/xdoor1/battery" ]
Type number : linkquality "linkquality" [ stateTopic = "zigbee2mqtt/xdoor1/linkquality" ]
}
}


Additional sensors can now easily be added to the bridge block. With a little more typing, sensors can also be defined outside the bridge block:


Thing mqtt:topic:MosquittoMqttBroker:BodySensor "Xiaomi Body Sensor" (mqtt:broker:MosquittoMqttBroker) @ "Location" {
Channels:
Type switch : occupancy "occupancy" [ stateTopic = "zigbee2mqtt/xbody1/occupancy", on="true", off="false" ]
Type number : voltage "voltage" [ stateTopic = "zigbee2mqtt/xbody1/voltage" ]
Type number : battery "battery" [ stateTopic = "zigbee2mqtt/xbody1/battery" ]
Type number : linkquality "linkquality" [ stateTopic = "zigbee2mqtt/xbody1/linkquality" ]
}


The existing channels can be found out via mosquitto_sub or journalctl. As soon as you stimulate a sensor, it sends all of this information to the Zigbee controller.



Applause
Of course, especially in combination with Zigbee (or Z-Wave), OpenHAB is a bottomless pit in terms of possibilities. A lot of technology can be connected even without a wireless connection: printers, mail and XMPP accounts, WLAN (or connected devices), telephone systems, mpd (Music Player Daemon), video cameras (e.g. via Zoneminder - but that would be a blog entry in itself) . With Zigbee everything gets even wilder. After the sensors, the entire rest of the house can be integrated, from lamps, heating and roller shutter control to the washing machine to the lawn mower to the wallbox of the electric vehicle.
If more Zigbee sensors / actuators are to be set up a little further away, you simply take a Raspberry Pi, connect another USB stick to it, install zigbee2mqtt and have the sensor data sent over the network to the MQTT broker on the OpenHAB machine .