2018-04-03

Add or remove items from a list of objects in a model attribute using Spring MVC and Thymeleaf

In a Spring MVC application I have a model with a List type attribute. The model is bound to a HTML form and rendered by Thymeleaf. The goal is to be able to add or remove items from the list and to submit the form with modified list. All of that with and without JavaScript.

There are few solutions for this problem available on the internet but none of those which I found was clean and simple enough. There has been some request interceptors, List interface custom implementations, etc.

The idea behind this solution is simple. In a controller there are four endpoints. Two are used for rendering and "saving" the model. Two for adding and removing items from the list. Later two can be invoked as a standard HTTP request or as an Ajax request. This will ensure that solution will work with or without JavaScript enabled.

Thanks to Thymeleaf's fragments there is only one template needed. By default, on an HTTP request all endpoints returns whole page. If Ajax request is sent, smaller part of the page containing the list will be returned.

Ability to not to re-render whole page on every HTTP request makes user experience more seamless. Following jQuery snippet will a) call the endpoints for adding or removing inems from list and b) replace #items fragment by returned content.

2017-11-03

Let's encrypt on Windows server + auto certificate bindings renewal

I am runnging a simple WCF Web HTTP service on a Windows 2008 box. There is a requirement to expose service's endpoints securely using HTTPS protocol. To do this I had to obtain a certificate from a certification authority. Recently, the Internet Security Research Group created free of charge authority, called Let's Encrypt. The authority allows you to create a certificate with validity that lasts few months and to renew generated certificates programatically. I decided to use Let's Encrypt certificates for my task.

Installation and set-up of Let's Encrypt client

  1. Install nginx and configure it as Windows service by using NSSM.

    To verify ownership of a domain, Let's Encrypt client has to expose a generated file on a certain unsecured (HTTP) URL of the server. Then the Let's Encrypt's server opens the URL, checks its content and verifies the domain. Easy way to do this is to configure nginx to run on port 80 of the server and to expose the URL.

  2. Configure nginx server documents from C:\inetpub\wwwroot\ directory on port 80. Don't enable secured communication on port 443, it would collide with WCF service.

  3. Install Letsencrypt-win-simple client. And run it by letsencrypt.exe.

    The client will generate certificates to a directory similar to this:

    Saving Certificate to C:\Users\myuser\AppData\Roaming\letsencrypt-win-simple\httpsacme-v01.api.letsencrypt.org\mydomain.com-crt.der
    Saving Issuer Certificate to C:\Users\myuser\AppData\Roaming\letsencrypt-win-simple\httpsacme-v01.api.letsencrypt.org\ca-0A0151530000025395736A0B85ECA708-crt.pem
    Saving Certificate to C:\Users\myuser\AppData\Roaming\letsencrypt-win-simple\httpsacme-v01.api.letsencrypt.org\mydomain.com-all.pfx
    

    But more importantingly it will store the certificate into system's keystore. To list the certificates you can run simple powershell command.

    powershell -Command Get-ChildItem Cert:\LocalMachine\My
    

Bind certificate with port 443 and configure binding auto-refresh

  1. Add new SSL certificate binding via Netsh command add sslcert.

    First, find the certificate fingerprint by a combination of powershell commands for listing and filtering.

    powershell -Command "Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.Subject -eq 'CN=mydomain.com'}"
    

    Use the returned thumbprint instead of %THUMBPRINT% for follwing command (appid can be random).

    netsh http add sslcert ipport=0.0.0.0:443 certhash=%THUMBPRINT% appid={e2eaacd9-92e6-43cc-b51c-7a7887149607}
    
  2. Create a batch script to refresh certificate binding.

    Use more elaborated powershell command to get thumbprint of a fresh certificate. Then remove existing binding and add new one.

    REM Get certificate thumbprint
    set GET_THUMBPRINT_COMMAND=powershell -Command "Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.Subject -eq 'CN=mydomain.com'} | Select -ExpandProperty Thumbprint"
    for /f %%i in ('%GET_THUMBPRINT_COMMAND%') do set THUMBPRINT=%%i
    
    
    REM Remove existing binding
    netsh http delete sslcert 0.0.0.0:443
    
    
    REM Add new binding
    netsh http add sslcert ipport=0.0.0.0:443 certhash=%THUMBPRINT% appid={e2eaacd9-92e6-43cc-b51c-7a7887149607}
    
  3. Plan execution of the script in Windows scheduler.

2017-10-22

The Big Five personality traits test

So, I started reading Daniel Nettle's book Personality, which describes popular five factor personality model.

At the beginning of the book there is a simple test that a reader is supposed to do before he will get deeper into individual traits.

Actually, I was curious not only about my own traits but also about traits of people close to me. So, I've created this electronic version of the test and I am going to send it to few people and ask them about their results. Also, I am going to ask them to fill the test, pretending to be me or somebody else we all know. Then I will do the same and I will compare the results and try to figure out what all that means. Of course if that's even possible.

The test

You have to complete all questions in following table to see the results. Or you can pre-fill the test from a code, if you have one:

Very uncharacteristic Moderately uncharacteristic Neither uncharacteristic nor characteristic Moderately characteristic Very characteristic
Starting conversation with a stranger
Making sure others are comfortable and happy
Creating an artwork, piece of writing, or piece of music
Preparing for things well in advance
Feeling blue or depressed
Planning parties or social events
Insulting people
Thinking about philosophical or spiritual answers
Letting things get into mess
Feeling stressed or worried
Using difficult words
Sympathizing with other's feelings

2017-09-17

New output directories in IntelliJ IDEA 2017 causes problems in Gradle tasks

To be able to add build cache, Gradle 4 introduced new compilation-output directory layout. Since version 4, every language present in your source set has its own output directory. This means that your Java file main/java/JavaClass.java will be compiled into build/classes/java/main/JavaClass.class and your Kotlin file will end up in build/classes/kotlin/main/KotlinClass.class. That is the reason, why Gradle's SourceSetOutput.getClassesDir() method, returning a single path became deprecated and a new method getClassesDirs() returning collection was added.

Unfortunately IDEA does not support multiple output directories per source set and Gradle's change caused some problems. To address that, JetBrains changed an existing output directory to new one that differs from Gradle's so there wouldn't be any interference.

Problem

The problem is, sometimes you need to access classes compiled by IDEA from a Gradle task. For example if you are doing an instrumentation or some kind of other byte code manipulation. In my case I am using ActiveJDBC and Ebean ORMs, which both needs to instrument compiled classes. Of course, I could use javaagent but that's not the point.

So, when I launch an application from IDEA, the process is following:

  1. Classes are compiled by IDEA, into out/classes/... directory (formerly it was build/classes/...).
  2. Gradle instrumentation task is executed. The task looks for compiled classes in build/classes/... but none is found.
  3. Application is started and fails shortly after with an error message "Are you sure, your classes has been instrumented?".

Solution #1 (insufficient in some cases)

To solve the problem, Vladislav Soronka of JetBrains suggested adding idea plugin into Gradle build script. The plugin allows to reconfigure IDEA's output directories.

apply plugin: 'idea'

idea {
    module {
        outputDir file('build/classes/main')
        testOutputDir file('build/classes/test')
    }
}

Unfortunately this sets the same output directory for classes and resources. So instead of having build/classes directory for your class files and build/resources for your resources, you will end up with everything in the build/classes. Of course, former build/resources directory is not goint to be put on classpath of Java application started by IDEA.

In my case this caused problems with JavaScript and CSS styles preprocessing, which I run as another Gradle task.

Solution #2 (final)

Best solution I was able to come with is to create a parametrized build. That means I've modified my instrumentation and resources preprocessing scripts to use one set of output directories when executed by Gradle command line client, and other set of directories when started from IDEA. In my case, I've used project properties to determine whether the build was started from IDEA. Environment variables instead of project properties can be used as well.

First of all, I've created idea.gradle build script. The script contains a simple resolve method. Gradle does not allow functions to be shared between build scripts, so I had to define it as a closure in an extension property.

// IDEA 2017 changed output directories from Gradle compliant build/... to
// out/... because of compatibility issues with Gradle 4. Because of that, the
// instrumentation, less compilation or JavaScript routes generation doesn't
// work.
//
// To fix the issue we need to tell postprocessing scripts where to find
// compiled classes and/or output resources (because of classpath). This
// can be done by passing -Pidea2017 argument to Gradle.
//
// gradle -Pidea2017 instrumentModels
//
ext {
    resolveOutputPath = { project, sourceSet ->
        gradleOutput = project.sourceSets.main.output
        idea2017OutputPath = project.file('out/production').toPath()
        boolean idea2017 = project.hasProperty('idea2017')

        switch (sourceSet) {
            case "classes":
                return idea2017 ? idea2017OutputPath.resolve('classes') : gradleOutput.classesDir.toPath()
            case "resources":
                return idea2017 ? idea2017OutputPath.resolve('resources') : gradleOutput.resourcesDir.toPath()
                break;
            default:
                throw new IllegalArgumentException("Unknown source set " + sourceSet)
        }
    }
}

Then, in my instrumentation build script I defined output directory simply by calling the closure. This is a snippet from my ActiveJDBC instrumentation build script:

apply from 'idea.gradle'

Instrumentation instrumentation = new Instrumentation()
instrumentation.outputDirectory = resolveOutputPath(project, "classes")
...

Finally, I specified project property in a "before launch" run configuration.

Gradle 4 and the future

As it was mentioned before, Gradle 4 introduced multiple output directories. That means: a method project.sourceSets.main.output.classesDir is currently marked as deprecated and will be removed soon. Some kind of refactoring towards new classesDirs method will be necessary in the future. For now everything should work without problems.

2017-07-20

Reference to a static method + instrumentation = fail!

In a Java project I am using ActiveJDBC ORM. Part of the ORM is an instrumentation process that adds new methods into compiled classes. In my case the instrumentation adds a few static factory methods into my Record class. For example Record.findAll() to get all records from the database.

I have an execute method that accepts a single parameter of type Callable. The method opens a connection, executes what's in the parameter and then closes it.

I implemented this a simple way. In a repository class, I just passed a reference to a static method of instrumented class into the execute method. After building the application in IntelliJ I got well known failed to determine Model class name, are you sure models have been instrumented? exception.

Even though, the model class was instrumented it didn't work. Then I made few simple changes in a repository class, not related to the execute method itself, and it started working. I was confused.

After a disassembly of repository class I noticed that there is a reference to Record's parent class instead of Record itself. Making small changes in repository and recompilation, fixed the reference.

Reason

  • First compilation went this way:

    1. Java compiled clean project (all classes were compiled).

      First, it compiled model class. The class was not instrumented so there were no static methods at this time.

      Then, it compiled repository class. Because model did not have any static methods at that time, a reference to a static method in the model class was "pointed" to a parent class method.

    2. ActiveJDBC plugin instrumented the model class but reference from repository to model's parent was already set, so it didn't make any change.

    At this moment, the bytecode of static method call looked like this:

    Method arguments:
      #33 ()Ljava/lang/Object;
      #34 invokestatic org/javalite/activejdbc/Model.findAll:()Lorg/javalite/activejdbc/LazyList;
      #35 ()Ljava/util/List;
    
  • Second compilation, after small changes in repository:

    1. Java compiled only the changed repository class. That means at this moment model class had static methods so the reference was set correctly.

    2. ActiveJDBC plugin instrumented the model class but that was not important at the moment.

    Now, the bytecode is correct:

    Method arguments:
      #33 ()Ljava/lang/Object;
      #34 invokestatic demo/Record.findAll:()Lorg/javalite/activejdbc/LazyList;
      #35 ()Ljava/util/List;
    

How to avoid this

Instead of reference to a static method use lambda method call.

So instead of this:

public List<Record> findAll() throws Exception {
    return execute(Record::findAll);
}

write this:

public List<Record> findAll() throws Exception {
    return execute(() -> Record.findAll());
}

In this case Java will not create a reference to parent class Model.findAll() but will point correctly to Record.findAll().

2017-06-28

Running Firefox in kiosk mode on Ubuntu Gnome 16.04

So we bought a few industrial all-in-one PCs from Chinese company QI YU TAI. These PCs came with pre-installed Windows, but because of possible legal issues, we decided to replace the Windows with Ubuntu.

Basic parameters of those PCs are:

  • Various Celeron processors.
  • Screen size from 12 inch, up to 19 inch.
  • eGalaxTouch resistive touch layer.
  • SSD hard disk.

In the beginning I planned to install Ubuntu with a default desktop environment, Unity. Unfortunately Unity lacks support for on screen keyboard, so I had to switch to Gnome Desktop, which seems to be the best option for a touch screen in the Linux world.

Installation

  1. Instal Ubuntu Gnome 16.04.

    Ubuntu homepage has several tutorials on creating a bootable USB stick.

  2. Calibrate touch screen and save calibration, so it will be restored on every boot.

    By default the touch screen has inverted axis. To calibrate it, follow tutorial on Ubuntu homepage https://wiki.ubuntu.com/Touchscreen

    I've used following scripts to load calibration on start of the system. But, for example, you can run xinput_calibrator without parameters, then follow printed instructions and store calibration as a X11 configuration.

    Optionally, you can install driver from touch screen manufacturer's site to support multitouch, gestures and more features http://www.eeti.com.tw/drivers_Linux.html

    Note: calibration of a rotated screen can be quite challenging. I had to do it manually by changing calibration values and by testing each configuration by hand.

  3. Turn off all power saving and "lock a screen" features of the system.

    • Settings -> Power: Turn off "Dim screen when inactive" option.
    • Settings -> Power: Set "Blank screen" option to never.
    • Settings -> Privacy: disable screen lock.
    • Disable DPMS (Energy Start) by executing xset s off -dpms. Actually you want to execute this command on each start of the system by adding it to Startup Applications.
    • Disable suspend, according to Debian.org sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
    • Disable a screen shield that makes you to unlock the screen when inactive for a while: https://github.com/lgpasquale/gnome-shell-extension-disable-screenshield
  4. Disable crash reports, so user won't be bothered by a "crash report message" when something exceptional happens.

    Do it by setting enabled property to 0 in /etc/default/apport file.

  5. Enable an on screen keyboard.

    • First you will need to install few packages, so QT applications like Firefox will have a support for touch.

      apt-get install qt-at-spi caribou
      
    • Settings -> Universal Access: Turn on "Screen Keyboard" option.

    Note: keyboard should disappear if you focus out of an input field. This does not wok in current release of qt-at-spi. It was fixed just recently https://bugzilla.mozilla.org/show_bug.cgi?id=789038#c12

  6. Show system's ip address on startup.

    I want to show system's IP address on startup so administrator can easily connect to the machine via network, so he can manage it without need of external keyboard. To do that, just create a simple script that will get an ip address and displays it as a notification bubble via notify-send application. Then add this script to Startup Applications.

    #!/bin/sh
    
    
    ip_addresses=$(ifconfig | grep -oE "inet addr:[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
    notify-send "$ip_addresses"
    
  7. Set up Firefox for kiosk mode.

    Install mKiosk plugin and configure it according to your needs.

    Install Click Drag Scroll extension so you can scroll a page by simply dragging it.

    Then add Firefox into Startup Applications.

  8. Install Unclutter and hide mouse cursor.

    apt-get install unclutter
    
    unclutter -idle 0.01
    

    Or you can add it into Startup Applications.

  9. Install SSH server to be able to connect to the machine remotely.

    sudo apt-get install openssh-server
    

2017-06-14

Running Chrome in kiosk mode on Windows 10 Home

For Factorify we wanted to create a touch screen where an employees could log their attendance. We got a cheap all-in-one PC from China (PiPO X9) with pre-installed Windows 10 Home and RFID card reader.

Our application is an web page. We wanted to start an application in a browser automaticaly and to prevent users from exiting it or doing anything harmful on the PC. At least users without a keyboard.

Configuration steps:

  • Create an user with limited permissions. So in case that anything goes horribly wrong he wouldn't be able to destroy the system.

    • Configure the system to automaticaly login as the newly created user. To do this you need to start a neplwiz application, select the user and disable the "users must enter a user name and password to use this computer" option.
  • Then, disable the Windows shell (tiles). To do this I decided to run a "dummy" program instead of explorer.exe, on system startup.

    Go to regedit and set the value for the following path:

    HKEY_CURRENT_USER/Software/Microsoft/Windows NT/CurrentVersion/Winlogon/Shell = rundll32

    • We added the value under the HKEY_CURRENT_USER because changing the value in HKEY_LOCAL_MACHINE would disable shell for all users using the computer.
    • Parameter Shell won't be probably present so you will need to create one.
    • Empty vaule doesn't work. So instead we execute rundll32 which actually does nothing.
  • Create a task in Task Scheduler that will start Chrome browser as soon as internet connection is available. To do this, start the Task Scheduler and create a new task. Configure "start only if the following network connection is available" option.

    Then define an action:

    cmd /C "chrome --incognito --disable-pinch --kiosk http://www.factorify.me/"

    • Chrome is executed by cmd so its started as a "different" process. That is because when connection gets unavailable the Task Scheduler actually kills the process it started.
    • Parameter --incognito prevents Chrome from showing an "application crashed bubble" in case of incorrect application exit. Incognito mode is good for kiosk mode anyway.
    • --disable-pinch disallows an user to zoom the page using multi-touch gestures.
  • Then fine-tune the system.

    • Disable the swipe gesture that can load a page from history. Do it by setting Chrome's chrome://flags/#overscroll-history-navigation parameter to disabled.
    • Configure an English keyboard layout so RFID reader can work properly.
    • Connect the PC to the internet.

Flaws, possible improvements:

  • Disable automatic Windows updates by turning off the update service.
  • PiPO X9 doesn't have any sort of internal battery. So when electricity goes off, your kiosk goes off as well. Newer version of the machine, the PiPO X10 solves the problem.
  • My original plan was to use Android system for the task. PiPO X9 actually has two system preinstalled. Windows 10 and Android 4.4. Unfortunately to create a kiosk in Android older than 5.x is kind of problematic. PiPO X10 ships with Android 5.1 and that should solve the problem.
  • There is a custom boot-loader that will let you chose a operating system on start of the machine. I did not manage to turn this off, so when restarted the machine will always show you system-select menu. The last started system is automatically selected after 10 seconds or so, but you have to wait.
  • I did not introduce any kind of health status monitoring of the machine and applications running on it. But, one thing that comes to my mind is: you can configure the URL where Chrome will send crash reports in case of fail. That would be useful, I guess.