Spring Boot/JPA/Hibernate annotations in Kotlin data classes

I’m still learning Kotlin, so I didn’t know this, but Kotlin has annotation use-site targets, like @get, @set, and @field.

I wanted to add timestamps via Hibernate, and they weren’t working. When I was trying to add a @(org.hibernate.annotations.)CreationTimestamp annotation and various other annotations without use-site targets, none of them were working. Couldn’t figure out why. Turns out, when you’re making a data class, you have to specify whether those annotations apply to getters, setters, fields, constructor parameters, etc.

A Kotlin data class that looks like this:

package com.example.test

import javax.persistence.*
import java.util.UUID
import org.hibernate.annotations.CreationTimestamp
import org.hibernate.annotations.UpdateTimestamp

@Entity
data class UselessThing(
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    val id: UUID?,
    
    val name: String = "",
    
    @CreationTimestamp
    @Column(updatable = false)
    val createdDate: Date = Date(),
    
    @UpdateTimestamp
    val modifiedDate: Date = Date()
)

Would probably translate to something like this in Java:
// imports omitted

public class UselessThing {
  private UUID id;
  private String name;
  private Date createdDate = Date();
  private Date modifiedDate = Date();  

  // This is a constructor.
  public UselessThing(
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        UUID id,

        String name,

        @CreationTimestamp
        @Column(updatable = false)
        Date createdDate,

        @UpdateTimestamp Date modifiedDate) {
    this.id = id;
    this.name = name;
    this.createdDate = createdDate;
    this.modifiedDate = modifiedDate;
  }

  /* getters, setters, copy methods and whatever else
  Kotlin data classes make */
}

Instead, You’d create a Kotlin data class that looks like this:

package com.example.test

import javax.persistence.*
import java.util.UUID
import org.hibernate.annotations.CreationTimestamp
import org.hibernate.annotations.UpdateTimestamp

@Entity
data class UselessThing(
    @field: Id
    @field: GeneratedValue(strategy = GenerationType.AUTO)
    val id: UUID?,
    
    val name: String = "",
    
    @field: CreationTimestamp
    @field: Column(updatable = false)
    val createdDate: Date = Date(),
    
    @field: UpdateTimestamp
    val modifiedDate: Date = Date()
)

The latter, I’m guessing, probably translates to something roughly like this in Java:

// imports omitted

public class UselessThing {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private UUID id;
  private String name;

  @CreationTimestamp
  @Column(updatable = false)
  private Date createdDate = Date();

  @UpdateTimestamp
  private Date modifiedDate = Date();  

  public UselessThing(UUID id, String name, Date createdDate, Date modifiedDate) {
    this.id = id;
    this.name = name;
    this.createdDate = createdDate;
    this.modifiedDate = modifiedDate;
  }

  /* getters, setters, copy methods and whatever else
  Kotlin data classes make */
}

The latter, unlike the former, also works.

I encountered another gotcha with timestamps. When the client tried updating a UselessThing (via the .copy() method to create a new object with updated properties and JpaRepository’s save() method to persist the object) and didn’t specify a createdDate, the created_date column in the database would get updated every time the entity was updated.

Or something like that. Going over that again now, there must have been something else going on.

Anyway, adding the @Column(updatable = false) annotation fixed that issue.

Bash scripting if statements. Also, running flow in a docker container for Visual Studio Code's flowtype.flow-for-vscode extension.

One of the more annoying things I encountered while learning bash scripting was the examples people used for if statements. Most of the tutorials and examples I followed used if statements with square brackets. Later, I learned that square brackets are only sometimes (or almost never) necessary.

Square brackets are just a shorthand form of the test command, and if just evaluates whether the expression/command that follows it returns a 0.

But commands' return values aren't displayed anywhere in plain sight, so then I learned about the special variable $?, which stores the return value of the previous command.

Basically, if you're using grep to determine whether or not something exists, wrapping it with the test command is unnecessary. grep already returns a 0 when it finds something, and it returns something else when it doesn't. Negating the return value of a grep command only requires that you prepend an exclamation point to the whole thing.

See example below for what I'm talking about. No square brackets anywhere in the if statements, because all I'm testing for is presence or absence of certain strings. Oh, and I wrote the script so Visual Studio Code could use flow from a docker container.

It was balking at my usual script containing just
#!/bin/bash
docker run --rm -v "$PWD":/src -w /src [image]:[version] [command] $*
which I used for lying that I had Golang installed on the system (where image and version were the name and version of a docker image derived from golang:1.9) and [command] was one of the commands Visual Studio Code was complaining about not having (and were contained in the docker image):
    go-outline: go get -u -v github.com/ramya-rao-a/go-outline
    go-symbols: go get -u -v github.com/acroca/go-symbols
    gocode: go get -u -v github.com/nsf/gocode
    godef: go get -u -v github.com/rogpeppe/godef
    godoc: go get -u -v golang.org/x/tools/cmd/godoc
    gogetdoc: go get -u -v github.com/zmb3/gogetdoc
    golint: go get -u -v github.com/golang/lint/golint
    gomodifytags: go get -u -v github.com/fatih/gomodifytags
    gopkgs: go get -u -v github.com/uudashr/gopkgs/cmd/gopkgs
    gorename: go get -u -v golang.org/x/tools/cmd/gorename
    goreturns: go get -u -v sourcegraph.com/sqs/goreturns
    gotests: go get -u -v github.com/cweill/gotests/...
    guru: go get -u -v golang.org/x/tools/cmd/guru
    impl: go get -u -v github.com/josharian/impl
    go play: go get -u -v github.com/haya14busa/goplay/cmd/goplay
There was one more that was not in that list that I also had to install, but I don't remember what it was. I can post the Dockerfile for the image later. Anyway, knowing about $? is also helpful for figuring out how you want to structure commands within if statements.
#!/bin/bash
LOG_FILE=/home/dogi/flow_log

function run_flow {
    echo "Creating flow container" >> $LOG_FILE
    docker run -dti --name "flow_server" -v "$PWD":"$PWD" -w "$PWD" vscode-node:8 bash
}

function start_flow {
    # Don't let crazy process spawners try to spawn flow repeatedly
    if docker ps -a | grep flow_server | grep seconds; then
        echo "Wait a minute" >> $LOG_FILE
        echo "`docker ps -a`" >> $LOG_FILE
        return
    fi

    # Run the flow container if it doesn't exist already.
    if ! docker ps -a | grep flow_server; then
        echo "Flow container doesn't exist; creating" >> $LOG_FILE
        echo "`docker ps -a`" >> $LOG_FILE
        run_flow
    # If the flow container exists, but was started in a different directory
    # (i.e., not found in mounts), destroy it and create it in the current one.
    elif ! echo $(docker inspect -f '{{ .Mounts }}' flow_server) | grep -w "$PWD "; then
        echo "Flow container has wrong mounts" >> $LOG_FILE
        echo "`docker inspect -f '{{ .Mounts }}' flow_server`" >> $LOG_FILE
        if docker ps -a | grep Up | grep flow_server; then
            echo "Flow container is running; killing" >> $LOG_FILE
            echo "`docker ps -a`" >> $LOG_FILE
            docker kill flow_server
        fi
        if docker ps -a | grep flow_server; then
            echo "Flow container exists; deleting" >> $LOG_FILE
            echo "`docker ps -a`" >> $LOG_FILE
            docker rm flow_server
        fi

        run_flow
    # Run the flow container if it already exists and was started in the current
    # directory
    elif ! docker ps -a | grep Up | grep flow_server; then
        echo "Flow container exists in the proper directory but isn't running; restarting container" >> $LOG_FILE
        echo "`docker ps -a`" >> $LOG_FILE
        docker start flow_server
    fi
}

# Suppress docker output. VS Code just wants flow's output.
start_flow > /dev/null

docker exec -i flow_server flow $*

Using Flow with create-react-app

If you're using create-react-app and want to use Flow, follow the instructions at this link: https://flow.org/en/docs/tools/create-react-app/ instead of the ones at this one.

Setting up a Spring Boot 2 app in Kotlin 1.2.21 on Fedora 27 with IntelliJ IDEA CE 2017.3, Gradle 4.5.1, and Auto-rebuild

Install gradle. I used the super safe method:

curl -s "https://get.sdkman.io" | bash
sdk install gradle 4.5.1

MitM attacks can't happen while you're using HTTPS. Not possible. (Definitely don't run the script as root.)

Fun fact: If you've just installed gradle, are confused why nothing's working in a Spring Boot project that for some reason can't find the main class, doesn't already have gradlew, and want to try running gradle with the gradle wrapper, just run

gradle wrapper
(via https://docs.gradle.org/4.5.1/userguide/gradle_wrapper.html)

and you'll have your own ./gradlew and ./gradlew.bat files in your project directory. It probably won't solve your problem. You can try adding this to your build.gradle file, but it may or may not help:

springBoot {
  mainClassName = 'com.example.ExampleApplication'
}
Anyway, getting started with Spring Boot with Kotlin. Checkout the source for a basic Spring Boot Kotlin project: https://github.com/sdeleuze/spring-boot-kotlin-demo

Add spring-boot-devtools to the list of dependencies. (Modify the dependencies section so it looks like this:
dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    compile("com.h2database:h2")
    compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    compile("org.jetbrains.kotlin:kotlin-reflect")
    compile("com.fasterxml.jackson.module:jackson-module-kotlin")
    testCompile("org.springframework.boot:spring-boot-starter-test") {
        exclude(module = "junit")
    }

    testCompile("org.junit.jupiter:junit-jupiter-api")
    testRuntime("org.junit.jupiter:junit-jupiter-engine")

    // Add this import
    compile("org.springframework.boot:spring-boot-devtools")
}

The build.gradle.kts file is written in, not Groovy, but Kotlin, using the Gradle Kotlin DSL plugin. Refer to the github repo for more documentation on using Kotlin with Gradle.

Then, add this import to the top of the build.gradle.kts file:
import org.springframework.boot.gradle.tasks.run.BootRun
And under
tasks {
...
}

Add
    withType<BootRun> {
        val sourceSets = the<JavaPluginConvention>().sourceSets
        sourceResources(sourceSets["main"])
    }

I'm not sure what that does or whether it's necessary, but it's copied from https://dzone.com/articles/continuous-auto-restart-with-spring-boot-devtools.

After that, run gradle build --continuous or grade build -t (same thing, just shorter) or ./gradlew build -t (different thing, but using gradle wrapper is recommended for some reason, so that's what I actually run) in a new terminal window (in the root directory of your project) to get automatic rebuilds running. You'll also need to run gradle bootRun or ./gradlew bootRun.

I've set up IDEA to run gradle bootRun for me by importing the project as a gradle project, choosing Edit Configuration under the Run menu, clicking the plus button to create a new Run configuration, adding my project, and putting bootRun in the Tasks box.

I ran into an issue while trying to get IDEA set up. The main error it kept giving me, after having pulled all the gradle dependencies, was not being able to find basic Java classes like Java.lang.String. That was solved by following the steps in this support forum post: https://intellij-support.jetbrains.com/hc/en-us/community/posts/206950935/comments/207150089.

Basically, make sure your SDKs are loaded properly. In my case, I had to refresh the JDK that was already detected (because I installed the JDK after installing and running IDEA). Also, make sure you have your JDK installed and not just your JRE. In Fedora, I ran dnf install java-1.8.0-openjdk-devel as specified here: http://openjdk.java.net/install/. It doesn't come with Fedora 27 by default.

Cheapish multiple monitor setup for a linux laptop with only one video output (without daisy chaining)

A DisplayLink adapter seems like the only thing that would have any chance of working. It would need this driver: https://github.com/displaylink-rpm/displaylink-rpm Further info about the driver is here: http://nothen.com.ar/en/support-for-displaylink-adapters-on-linux/ and here http://www.displaylink.com/downloads/ubuntu
These are some adapters that might be able to be used: http://www.displaylink.com/products/find?cat=3&usba=1, but note that they have to be from one of these four device families (as per displaylink-rpm@github):
Here are some 27" options: