Missed Demo for the Japanese Students I Gave Speech to

I had an opportunity to give a speech to students who are looking to become engineers in Japan last Friday. It was great to meet them though it was online. They seemed eager to learn what takes to be engineers. I was honored to give speech to them. They were also learning English, so this may be good for them.

Due to my unpreparedness on my side, I missed one demo. I was basically compiling Java code and decomplie it with IntelliJ. I would like to show how it can be done here in this blog for their view.

First, create a text file (helloworld.java) with the following code. It’s just a simple hello world program in Java.

public class helloworld {
    public static void main(String args[])
        System.out.println("Hello World");

Once you have the file, compile it like the following from your terminal (command line).

javac helloworld.java

The javac (Java compiler) compiles the text file to Java byte code (helloworld.class). You can execute the hello world program like the following.

java helloworld


Hello World

When you open helloworld.class file with IntelliJ, you can decompile it.

Decompilation is not really a useful technique anymore because of the current trend of open source but if the source code is closed but you want to learn how the Java program works, it’s still an interesting technique to use especially while you are learning how program works.

I believe JetBrain provides students with free license so you may be able to use the IDEs for free.

The reason why I could not find the helloworld.class file at the time of demo was because I was using WSL 2 on Windows. I had the file on the Linux side of the OS but I had forgotten to copy the file on the Windows side. I’m so sorry about it.

There was so much more I wanted to talk to everyone about but our time was limited. I wish all of you successful careers and bright future. 🙂

How to Install Jenkins Slave as Windows Service

It is time to review this article. “How to Install Jenkins Slave as Windows Service” has been one of the most popular blog article on this site. I just went through the steps because I needed to have a permanent Windows slave and I found myself trip over some steps because of some lack of information.

Here I am going to review and enhance the article.


OpenJDK or Oracle Java (We’ll use OpenJDK in this article)

  • Java
  • Windows

Install OpenJDK

  • Download OpenJDK 17 (the latest one did not work).
  • Download the zip for Windows.
  • Unzip the contents and place all the files under C:\Users\[your username]\jdk (or it could be placed somewhere else if you want to)
  • From the Windows menu, enter “env” to show “Edit the system environment variables” menu and select it.
  • Click “Environment Variables” button.
  • Under System Variables, click New… button and enter JAVA_HOME for Variable name and the path to the JDK root path.
  • Click OK and close Environment Variables dialogue.
  • Open command line and make sure you can get an output from java.
    %JAVA_HOME%\bin\java --version

Create a Node

  • Logon to Jenkins with an administrative account.
  • Go to Manage Jenkins.
  • Click Manage Nodes and Clouds.
  • Click New Node.
  • Enter node name like “win-bld01” and click Permanent Agent and then click OK.
  • Enter a name like win-bld01 (or whatever you like) and enter the path to the directory where you plan to have Jenkins slave executable. In this example, I choose C:\Users\hiriu\jenkins and enter the same path for Custom WorkDir Path.
  • Click OK to save the setting. We’ll revisit it afterwards.

Jenkins Slave Download and Install

  • Download the latest Windows Service wrapper executable from https://repo.jenkins-ci.org/ui/native/releases/com/sun/winsw/winsw/
  • In this example, we will get winsw-2.9.0-net461.exe
  • Place the executable under C:\Users\hiriu\jenkins and rename it as jenkins-slave.exe
  • Create jenkins-slave.xml in the same directory with the following XML. Make sure to change the values in the XML according to your environment. You can get the jnlpUrl from the Configure page of the node.
  <name>Jenkins agent</name>
  <description>This service runs an agent for Jenkins automation server.</description>
  <arguments>-Xrs -jar &quot;%BASE%\slave.jar&quot; -jnlpUrl https://hoge.com/computer/win-bld01/jenkins-agent.jnlp -secret 18f9c0c497c0997e9f65buhie743868ae6d5e2e78a6ba77 -workDir "C:\Users\hiriu\jenkins"</arguments>
  <onfailure action="restart">
    <download from="https://hoge.com/jnlpJars/slave.jar" to="%BASE%\slave.jar">
        <extension className="winsw.Plugins.RunawayProcessKiller.RunawayProcessKillerExtension" enabled="true" id="killOnStartup">

For more options, please refer to the official documentation.

Also make sure to create jenkins-slave.exe.config file with the following XML cotent to prevent the executable from running on the earlier version of the .NET Framework.

		<supportedruntime version="v4.0"/>

Execute the following command to install the Jenkins slave as Windows Service.

jenkins-slave.exe install

Once the installation is successful, you should see the output like below.

Also, you should be able to see “Jenkins agent” item in Windows Services console. You can open the Windows Services console by entering services.msc

Trust the SSL Certificate

The HTTP connection between your client computer and your Jenkins instance may be protected by SSL. When you try to connect your Jenkins slave via HTTPS (SSL) connection, it may fail with the following error.

Exception in thread "main" java.io.IOException: Failed to validate a server certificate. If you are using a self-signed certificate, you can use the -noCertificateCheck option to bypass this check.

It is optional to add -noCertificateCheck option in your jenkins-slave.xml file, but it is better for your Java process to trust the certificate and use the SSL connection to protect the data. To accomplish that, please follow the steps below.

  1. Open terminal.
  2. Execute openssl s_client -showcerts -connect hoge.com:443 < /dev/null | openssl x509 -outform DER > hoge.com.cer
  3. You obviously have to have openssl on your machine. Install Cygwin if you are on Windows and that will give you an ability to execute openssl. Or you could use Linux Subsystem for Windows.
  4. Once you get the .cer file, it’s time to tell Java to trust the certificate. For that, we use keytool.
  5. Execute the following command. If you are on Linux Subsystem, you may want to go back to the normal command prompt.
    %JAVA_HOME%\bin\keytool -trustcacerts -keystore "%JAVA_HOME%\lib\security\cacerts" -storepass changeit -alias ppd -import -file "C:\Users\hiriu\Downloads\hoge.com.cer"
  6. You will be prompted to enter “yes”. Once you enter yes, you are ready to start the Jenkins agent Windows Service.
  7. Navigate to Windows Service console (services.msc) and start the Jenkins agent Windows service.
  8. Once the communication between Jenkins slave and the Jenkins master is successful, you should see the node icon turning to normal like the image below.

Bulk Create Jenkins Nodes

If permanent slaves are required and hundreds of them, it’s very manual and tedious tasks. I have created a PowerShell script to create Jenkins nodes in bulk. CloudBees’ version of Jenkins seems to have nice API to create nodes but open source/free version doesn’t seem to have one though Jenkins CLI could be utilized to accomplish it. Please follow my previous blog article to make sure your Jenkins CLI works before moving on with this article. Please note that this script does not install Jenkins slaves on separate machines but I am planning to create a script to do that in the future.


  • Windows (The script I am going to introduce utilizes PowerShell, so Windows is the safest choice of OS.)
  • Java
  • Donwloaded jenkins-cli.jar

The PowerShell Script Execution

The PowerShell script that I wrote can generate nodes in bulk. It can be found here in GitHub.

For example, execute the script with the parameters like below.

.\Jenkins_BulkCreateNodes.ps1 -JenkinsServerUrl https://jenkins.linux-mint.local/ -Username myadminuser -ApiToken 1197adsfasdfadsfasdfasdfasdfasdf3a -NodeCount 100 -NodeNamePattern "winnode-###" -OverwriteNodeIfExists $True

The command above creates nodes in Jenkins master from winnode-001 to winnode-100. The red cross icon you see below only indicates that they are offline and no actual slaves is communicating with them yet.

You can change the naming pattern specifying -NodeNamePattern.

By specifying -OverwriteNodeIfExists parameter to $True, it can delete the node and recreate. It can be dangerous, so its default value is $False and it’s an optional parameter.

The PowerShell Code

I am pasting all the code I have written for this operation. But if you just want the file, please go to my GitHub repo. All the details of each parameters are documented in the code.

       Generates Jenkins nodes.
        This script bulk generates Jenkins nodes on the master so that nodes will be ready for Jenkins slaves to talk to them. 
        It utilizes Jenkins CLI, so Java (https://java.com/en/download/) and downloaded jenkins-cli.jar file from Jenkins master is required.
    .PARAMETER JenkinsServerUrl
        Required. Base URL of the Jenkins master. e.g. https://myjenkins.com/
    .PARAMETER Username
        The username to use for this process.
    .PARAMETER ApiToken
        Generated API Token of the username.
    .PARAMETER NodeCount
        Specifies how many nodes to created.
    .PARAMETER NodeNamePattern
        Specify the pattern of node name here. If 20 nodes to be created and "foobar-###" is passed, foobar-001 to foobar-020 would be generated.
    .PARAMETER Description
        Optional. Specify the description of each node. Use this field to specify what kind of fleet you are creating.
    .PARAMETER RemoteRootDir
        Optional. Defaults to "C:\slave". Specify any directory where you expect slave bits to reside on each node.
        Optional. Defaults to NORMAL.
    .PARAMETER NumExecutors
        Optional. Defaults to 1.
    .PARAMETER Labels
        Optional. e.g. "WINDOWS 2019". Separate labels by space.
    .PARAMETER JenkinsCliJarFilePath
        Optional. When you download jenkins-cli.jar, the file is usually at ~/Download/jenkins-cli.jar. Downaload the file from https://your-jenkins-server/jnlpJars/jenkins-cli.jar
        There is a download link on https://your-jenkins-server/cli
    .PARAMETER OverwriteNodeIfExists
        Optional. If the node to be created already exists on the Jenkins master, it deletes it and recreates it if $True is passed. If $False, it is skipped.
    .PARAMETER KeepNodeXmlFiles
        Optional. Defaults to $False. If $True is passed, the XML files created for the nodes will be kept in the same directory as this script. This option could be used to reuse the XML files in different environments.
    [int]$NodeCount = 5,
    [string]$NodeNamePattern = "winnode-###",
    [string]$Description = "Bulk gen'ed node",
    [string]$RemoteRootDir = "C:\slave",
    [string]$Mode = "NORMAL",
    [int]$NumExecutors = 1,
    [string]$Labels = "",
    [string]$JenkinsCliJarFilePath = "$env:HOMEPATH\Downloads\jenkins-cli.jar",
    [bool]$OverwriteNodeIfExists = $False,
    [bool]$KeepNodeXmlFiles = $False

$digitCount = ($NodeNamePattern.ToCharArray() | Where-Object {$_ -eq '#'} | Measure-Object).Count

ForEach ($index In 1..$NodeCount)
    $number = "{0:D$digitCount}" -f $index
    $nodeName = $NodeNamePattern -replace "#{$digitCount}", $number 
    $xmlSettings = New-Object System.Xml.XmlWriterSettings
    $xmlSettings.Indent = $true
    $xmlSettings.Encoding = [System.Text.Encoding]::UTF8
    $xmlSettings.OmitXmlDeclaration = $True
    $xmlSettings.ConformanceLevel = "Document"

    $sw = New-Object System.IO.StringWriter
    $xw = [System.Xml.XmlWriter]::Create($sw, $xmlSettings)

        $xw.WriteElementString("name", $nodeName)
        $xw.WriteElementString("description", $Description)
        $xw.WriteElementString("remoteFS", $RemoteRootDir)
        $xw.WriteElementString("numExecutors", $NumExecutors)
        $xw.WriteElementString("mode", $Mode)

        $xw.WriteAttributeString("class", 'hudson.slaves.RetentionStrategy$Always')
        $xw.WriteEndElement() # retentionStrategy

        $xw.WriteAttributeString("class", "hudson.slaves.JNLPLauncher")

        $xw.WriteElementString("disabled", "false")
        $xw.WriteElementString("internalDir", "remoting")
        $xw.WriteElementString("failIfWorkDirIsMissing", "false")
        $xw.WriteEndElement() # workDirSettings
        $xw.WriteElementString("websocket", "false")
        $xw.WriteEndElement() # launcher

        $xw.WriteElementString("label", $Labels)
        $xw.WriteElementString("nodeProperties", "")
        $xw.WriteEndElement() #slave
    catch [System.Exception]
        Write-Host $_
        exit 1

    # Write out the node XML to file to be used for standard input below.
    Set-Content -Path "$nodeName.xml" -Value $sw.ToString() -Force

    # Check the existence of the node.

    $processInfo = New-Object System.Diagnostics.ProcessStartInfo
    $processInfo.FileName = "java"
    $processInfo.RedirectStandardError = $True
    $processInfo.RedirectStandardOutput = $True
    $processInfo.RedirectStandardInput = $True
    $processInfo.Arguments = "-jar $JenkinsCliJarFilePath -s $JenkinsServerUrl -auth $Username`:$ApiToken get-node $nodeName"
    $processInfo.UseShellExecute = $False

    $process = New-Object System.Diagnostics.Process
    $process.StartInfo = $processInfo
    $process.Start() | Out-Null
    $stdo = $process.StandardOutput

    If ($process.ExitCode -ne 0)
        Start-Process -FilePath java -NoNewWindow -Wait -ArgumentList "-jar $JenkinsCliJarFilePath","-s $JenkinsServerUrl","-auth $Username`:$ApiToken","create-node" -RedirectStandardInput "$nodeName.xml"
        Write-Host "$nodeName created ($(Get-Date))"
        If ($OverwriteNodeIfExists)
            Write-Host "Deleting $nodeName..."
            Start-Process -FilePath java -NoNewWindow -Wait -ArgumentList "-jar $JenkinsCliJarFilePath","-s $JenkinsServerUrl","-auth $Username`:$ApiToken","delete-node $nodeName"

            Write-Host "Recreating $nodeName..."
            Start-Process -FilePath java -NoNewWindow -Wait -ArgumentList "-jar $JenkinsCliJarFilePath","-s $JenkinsServerUrl","-auth $Username`:$ApiToken","create-node" -RedirectStandardInput "$nodeName.xml"
            Write-Host "Node $nodeName exists."   

        If (!$KeepNodeXmlFiles)
            Remove-Item -Path "$nodeName.xml" -Force

Next Step

The script I created can bulk create the “receivers” for the actual slaves to talk to the Jenkins master. I will write another PowerShell to script to automatically install and communicates with the Jenkins master.


Pull Requests, any suggestion or even forking is welcome. This should make Jenkins permanent node/slave administration a little easier.

Configuring JavaFX Project on IntelliJ

I’ve had a hard time just to get a JavaFX project going with IntelliJ on Java 11. After spending 2 nights of digging and Googling, I finally got “Hello World” window to show up.

I will try to summarize what I have done.


I have decided to manage dependencies and build with maven. Here is the pom.xml file I created. Right click on the project in Project window and select All Frameworks Support to add Maven support to the project.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">



VM options

I’m sure the module path may be different depending your installation but here is the VM options I gave in Run/Debug Configurations. (Run -> Edit Configurations) This is necessary because JDK does not contain JavaFX by default anymore.

--module-path "/usr/share/openjfx/lib" --add-modules javafx.controls,javafx.fxml


Navigate to File -> Project Structure -> Project. Make sure you select 11 – Local variable syntax for lambda parameters in Project language level.

Language Level

Navigate to File -> Project Structure -> Modules. Select 11 – Local variable syntax for lambda parameters for Language level in Sources tab.11 - Local variable syntax for lambda parameters

Target bytecode version

Navigate to File -> Settings -> Build, Execution, Deployment -> Compiler -> Java Compiler. Select 11 for Target bytecode version.

Target bytecode version


There are many options to create cross platform desktop applications and JavaFX is one of them. I hope JetBrain will streamline the way to create JavaFX project easily in the future. I have uploaded the code to my GitHub repository. https://github.com/hiriumi/jenkins-toolset-fx

Integer Array Intersection (Improved)

Right after I wrote the previous blog entry regarding integer array intersection, I thought of sorting the two parameters before I did anything with those arrays. I think I came up with a better algorithm. Let’s look at the code.

    public void testIntersect2()
        int[] nums1 = {4,7,9,7,6,7};
        int[] nums2 = {5,0,0,6,1,6,2,2,4};

        int[] result = intersect2(nums1, nums2);
        for (int i : result)

    private int[] intersect2(int[] nums1, int[] nums2)

        int[] shorter;
        int[] longer;

        if (nums1.length < nums2.length) {
            shorter = nums1;
            longer = nums2;
        else {
            shorter = nums2;
            longer = nums1;

        int[] nums = new int[shorter.length];

        int j = 0;
        int index = 0;

        for(int i = 0; i < shorter.length; i++) {
            while (j < longer.length && shorter[i] >= longer[j]) {
                if (shorter[i] == longer[j]) {
                    nums[index] = shorter[i];

        int[] result = new int[index];
        for(int i = 0; i < index; i++)
            result[i] = nums[i];

        return result;

What’s better about the second attempt on this algorithm is I did not create an ArrayList for longer variable. It takes less space. The only extra data in the memory is the result as int[] so space complexity-wise it’s O(n+1) = O(n). Also convering ArrayList<Integer> to int[] seems to take some time. When I measured the code execution time, my previous code took 34 ms but this one took only 1 ms. It doesn’t seem much because it’s only 34 ms with the less efficient code but if the system execute the function millions and billions of times, it adds up!

So the logic is that you iterate through the sorted shorter integer array from the first element and you move each element in the longer one until the element matches the one in the longer array or the value in the shorter array is greater than equal to the current element in the longer array.

With this algorithm, we are iterating the shorter variable once and the longer one just about once (or less). The time complexity may be O(n).

After all, sorting the integer array at the beginning was a good idea. 🙂

Integer Array Intersection

Let’s think about 2 integer arrays.

int[] nums1 = {1, 2, 3, 9, 9, 9, 8, 5};
int[] nums2 = {9, 9};

Need to write a function that returns the common elements in both arrays. In this example, nums2 has two 9’s and nums1 has three 9’s, so the function must return {9, 9} (two 9’s).

I’ve solved the problem but I’m not sure if I have done it in the most efficient way. I will show my code below.

    public void testIntersect()
        Integer[] nums1 = {9, 6, 8, 9, 9};
        Integer[] nums2 = {9, 8, 9, 5, 5, 5, 5};

        int[] result = intersect(nums1, nums2);
        for (int i : result)

    public int[] intersect(Integer[] nums1, Integer[] nums2) {
        List<Integer> longer;
        Integer[] shorter;
        if (nums1.length < nums2.length) {
            longer = Arrays.stream(nums2).collect(Collectors.toList());
            shorter = nums1;
        else {
            longer = Arrays.stream(nums1).collect(Collectors.toList());
            shorter = nums2;

        List<Integer> result = new ArrayList<>();

        for(int i = 0; i < shorter.length; i++) {
            if (longer.contains(shorter[i])) {
        return result.stream().mapToInt(i -> i).toArray();

The first thing I thought of doing is that I want to iterate through the shorter array so I declared longer as List<Integer> and shorter as just an integer array. The reason the longer variable is declared as List<Integer> is because I need to be able to remove elements from it. The logic is that if an element in the shorter array, the element in the longer array should be removed because if shorter contains two 9’s and longer array contains a single 9, the second 9 in the shorter array should not match the 9 in the longer array.

result variable as List<Integer> contains the matched elements and it finally returns the array.

The time complexity for this code may be O(n) and the space complexity may be O(2n).

I’m thinking there may be cleverer ways to accomplish this…

Reverse Integer

I’m doing some algorithm practice to improve my programming skills and to keep myself sharp. It’s really refreshing for an engineer like me who is so busy with day-to-day tasks which doesn’t require much of sophisticated algorithms though my job involves coding for automation.

In this article, I’m going to reverse integers. For example, if you receive 12345 and then function must return 54321. The function must handle the case where if the result overflows the integer max or min value.

I initially came up with a way to get absolute value, convert it to a string, reverse it and if the original value is negative, insert “-” to the index 0 of the string. Finally if you parse the string to integer, you get the result. This is not an ideal solution for several reasons. Here is the code.

    public void testPoorReverseInteger()
        int reversed = poorReverseInteger(12345);
        assertEquals(reversed, 54321);

        reversed = poorReverseInteger(-12345);
        assertEquals(reversed, -54321);

    private int poorReverseInteger(int input)
        StringBuilder s = new StringBuilder(Integer.toString(Math.abs(input)));
        s = s.reverse();
        int result = 0;

        if (input < 0)
            s.insert(0, "-");

            result = Integer.parseInt(s.toString());
        catch(NumberFormatException exp)
            return 0;

        return result;

I think the time complexity is probably O(n) but the space complexity is O(2n). I’m also using exception for handling overflow situation and using exception for validation is relatively expensive. If you expect the exception to happen very often, the time complexity will add up. Also converting from integer to string and string back to integer is also not such an efficient process.

Here is another smarter way to reverse an integer.

    public void testReverseInteger()
        int input = -12345456;
        int result = reverseInteger(input);
        assertEquals(result, -65454321);

    private int reverseInteger(int input)
        //max integer 2147483647
        //min integer -2147483648
        int result = 0;
        while (input != 0) {
            int lastDigit = input % 10;
            input /= 10;

            if (result > Integer.MAX_VALUE/10 || (result == Integer.MAX_VALUE/10 && lastDigit > 7))
                return 0;

            if (result < Integer.MIN_VALUE/10 || (result == Integer.MIN_VALUE/10 && lastDigit < -8))
                return 0;

            result = result * 10 + lastDigit;

        return result;

Declaring int result = 0; is no brainer. We will use it to keep the result for returning the value.

The logic of the function is to get the value of the last digit (line# 15) by getting a remainder of division by 10, reduce the value of input by dividing it by 10, check the overflow situation (from line# 18 to 26) and add it to the result variable. So for the first iteration of 12345, the last digit is 5. When 12345 is divided by 10, the value is reduced to 1234 because the variable is integer. The decimal point is omitted. Then in the next iteration, 4 is contained in the last digit. The result variable contains 5 at the point of hitting the line# 28, 5*10 + 4 = 54. If you repeat this process until input is 0, you will get 54321.

Let’s talk about handling the overflow situation.

if (result > Integer.MAX_VALUE/10 || (result == Integer.MAX_VALUE/10 && lastDigit > 7))

If the value of result variable is greater than the value of 1/10 of the max integer value, the result will be definitely larger than the max integer value, so there will be a overflow situation on line# 28.

Another situation that needs to handle is if the value of result variable is equals to the 1/10 of the max integer value and the last digit is larger than 7. The value of max integer is 2147483647 so if the value of lastDigit variable is greater than 7, the result will be greater than 2147483647. The validation for the min integer value is the same idea.

The latter method is better in a sense that the space complexity is O(n) but the time complexity is O(1) because it only deals with integer which is limited to 32 bit data. It also doesn’t depend on exception for overflow validation.


Knowing the nature of the data you are dealing with makes it more efficient to work with it. I’m planning to have more fun with algorithm especially in Java.

Reverse String

Changing from install and configuring server products to some coding.

Let’s reverse a char array.

Input: [“a”, “b”, “c”, “d”, “e”, “f”]
Output: [“f”, “e”, “d”, “c”, “b”, “a”]

I’m writing the code in Java.

    public void reverseString(char[] s) {
        int i = 0;
        int j = s.length - 1;
        while (i < j)
            char tmp = s[i];
            s[i] = s[j];
            s[j] = tmp;

This algorithm is probably O(n) in time complexity and O(1) in space complexity as it does not create another set of array to store the result.

Just a little bit of fun of coding. 🙂