Fun with Time Zones in Java 8

They say that one way to identify a software developer is to whisper the word “timezone” in their ear and see if they shudder.

That’s certainly true for me, though my reaction is based more on travel and trying to arrange conference calls across time zones than actual coding. Like most scary things, I’ve tried to avoid the whole date/time API in Java, partly because prior to Java 8 the API is a tire fire and partly because the whole issue is like the “Here be dragons” section of a map.

herebedragons

Recently, however, I’ve been teaching Java 8 upgrade classes, and making Java 8 presentations at conferences on the No Fluff, Just Stuff tour. As part of those talks, I give an overview of the new java.time package.

The new package, by the creators of JodaTime, finally (finally!) provides an alternative to java.util.Date and java.util.Calendar. New classes like java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime and java.time.ZonedDateTime are now all available and much more powerful. If you used JodaTime in the past (no pun intended, but they’re hard to avoid), you’re already familiar with them, as the same people who wrote JodaTime in the first place wrote the new package.

I’m certainly not going to review the whole thing here, but I did want to mention a couple of fun examples.

First, I’ve known for some time that there are time zones in the world that are off by half-hour offsets rather than whole hours. To pick one, Indian Standard Time is UTC+05:30. When I mentioned that in class, I also said that someone once told me that there was a time zone in the world offset by 45 minutes. At the time I thought they were pulling my leg, but now I have the machinery to find out.

Once problem, however, is that abbreviations like EST or IST are no longer valid. The Wikipedia article on Time Zones discusses the issue, which claims that “such designations can be ambiguous”, where ECT could stand for Eastern Carribean Time, Ecuador Time, or even European Central Time. Instead, the ISO 8601 standard uses either offset designators, like UTC-05:00, or “region-based IDs”, like “America/New_York”.

(Speaking of the ISO 8601 standard, since there’s an XKCD cartoon on everything, here’s the one on that: https://xkcd.com/1179/ .)

Bringing it back to Java, the API defines a class called java.time.ZoneId, which has a static method called ZoneId.of(...) that takes a designator. You use that to create a ZonedDateTime. If you use an offset as the argument, then the time in the ZonedDateTime does not change, but if you use the region, the time will automatically adjust for Daylight Savings Time rules in that region.

[As you can imagine, the whole Daylight Savings Time issue is another rabbit hole I choose not to dive into. Those rules are discussed in a class called java.util.time.zone.ZoneRules, which refers to classes like ZoneOffsetTransition, ZoneOffsetTransitionRule, and ZoneRulesProvider. You can see how the complexity just goes up and up, especially because DST rules change frequently in different locations. Yikes.]

If you know the region ID, you can create a ZoneId using the of method. I have the opposite problem, however. I want to figure out the region ID given the offset.

Fortunately, the Java Tutorial has a section on ZoneId and ZoneOffset that actually addresses this problem. For some strange reason, however, their sample code doesn’t use the Java 8 streams and lambda expressions, so I decided to rewrite it. Here’s my version:

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.List;

import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.toList;

public class FunnyOffsets {
    public static void main(String[] args) {

        LocalDateTime now = LocalDateTime.now();
        List<ZoneId> zoneIds = ZoneId.getAvailableZoneIds().stream()
                .map(ZoneId::of)
                .filter(zoneId -> {
                    ZoneOffset offset = now.atZone(zoneId).getOffset();
                    return offset.getTotalSeconds() % (60 * 60) != 0;
                })
                .sorted(comparingInt(zoneId ->
                        now.atZone(zoneId).getOffset().getTotalSeconds()))
                .collect(toList());

        ZonedDateTime current = now.atZone(ZoneId.systemDefault());
        System.out.println("Current time is " + current);
        System.out.printf("%10s %20s %13s%n", "Offset", "ZoneId", "Time");
        zoneIds.forEach(zoneId -> {
            ZonedDateTime zdt = current.withZoneSameInstant(zoneId);
            System.out.printf("%10s %25s %10s%n", zdt.getOffset(), zoneId,
                zdt.format(
                    DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)));
        });
    }
}

That code requires some explanation. First, the ZoneId.getAvailableZoneIds() method returns a Set of Strings containing all the region IDs. After converting to a Stream, the map(ZoneId::of) expression transforms that into a stream of ZoneId instances.

Then I want to filter that stream to return only those ZoneIds that have an offset that isn’t evenly divisible by 3600 (= 60 sec/min * 60 min/hr). To get the offset, however, you need a ZonedDateTime, so I use the current LocalDateTime and use the atZone method with each ZoneId to get a ZonedDateTime, and then call its getOffset method. That, in turn, has a getTotalSeconds method, and I can do the modulus on that.

I expected there has to be an easier way, but this does work. At that point, I could have just printed them, but I decided to sort them by offset first.

The sorted method on Stream takes a java.util.Comparator. I could implement the Comparator as a lambda myself, but Java 8 also added several default and static methods to that interface. One of them is Comparator.comparingInt, which takes an ToIntFunction that transforms its argument into an int. Then sorted generates a Comparator that sorts the ints, which then sorts the collection based on the results.

Believe it or not, that whole map/filter/sorted paradigm gets much easier with practice. It was harder for me to write that explanation than to figure out the method calls.

To print the results, I wanted to show the offset in each time zone as well as its region name. The ZonedDateTime class has a method called withZoneSameInstant, which converts a given time to its equivalent in another time zone.

(That’s a very convenient method that I’ve needed my entire professional career, and justifies all the time (again, no pun intended) I’ve spent on this.)

Finally, printing them out was easier if I formatted the time, for which I used the DateTimeFormatter shown. The result right now is:

Current time is 2016-07-16T16:12:51.905-04:00[America/New_York]
    Offset               ZoneId          Time
    -09:30         Pacific/Marquesas   10:42 AM
    -04:30           America/Caracas    3:42 PM
    -02:30          America/St_Johns    5:42 PM
    -02:30       Canada/Newfoundland    5:42 PM
    +04:30                      Iran   12:42 AM
    +04:30               Asia/Tehran   12:42 AM
    +04:30                Asia/Kabul   12:42 AM
    +05:30              Asia/Kolkata    1:42 AM
    +05:30              Asia/Colombo    1:42 AM
    +05:30             Asia/Calcutta    1:42 AM
    +05:45            Asia/Kathmandu    1:57 AM
    +05:45             Asia/Katmandu    1:57 AM
    +06:30              Asia/Rangoon    2:42 AM
    +06:30              Indian/Cocos    2:42 AM
    +08:45           Australia/Eucla    4:57 AM
    +09:30           Australia/North    5:42 AM
    +09:30      Australia/Yancowinna    5:42 AM
    +09:30        Australia/Adelaide    5:42 AM
    +09:30     Australia/Broken_Hill    5:42 AM
    +09:30           Australia/South    5:42 AM
    +09:30          Australia/Darwin    5:42 AM
    +10:30       Australia/Lord_Howe    6:42 AM
    +10:30             Australia/LHI    6:42 AM
    +11:30           Pacific/Norfolk    7:42 AM
    +12:45                   NZ-CHAT    8:57 AM
    +12:45           Pacific/Chatham    8:57 AM

So not only are there regions with half-hour offsets, like “Canada/Newfoundland”, “Australia/Adelaide”, and “Pacific/Norfolk”, there are indeed time zones offset by 45 minutes, like “Asia/Katmandu”, “Australia/Eucla”, and “Pacific/Chatham”.

I haven’t been able to find the reasons for all the odd offsets, but they appear to be due to political compromises between two surrounding zones. Some are very recent adoptions, like the Mongolian one (“Asia/Kathmandu”), which wasn’t established until 1986.

On the guiding principle that anything I can do in Java I can do much more easily in Groovy, I decided to write a Groovy version. In this case, the Groovy JDK hasn’t done anything with the classes in java.time yet. Still, the normal Groovy simplifications lead me to this version:

import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle

LocalDateTime now = LocalDateTime.now();
List<ZonedDateTime> zdts =
    ZoneId.availableZoneIds
        .collect { now.atZone(ZoneId.of(it)) }
        .findAll { it.offset.totalSeconds % (60 * 60) != 0 }
        .sort { it.offset.totalSeconds }

ZonedDateTime current = now.atZone(ZoneId.systemDefault());
println "Current time is $current"
printf("%10s %20s %13s%n", "Offset", "ZoneId", "Time")
zdts.each {
    ZonedDateTime zdt = current.withZoneSameInstant(it.zone)
    System.out.printf("%10s %25s %10s%n", zdt.offset, it.zone,
        zdt.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)))
}

I could have used the same map/filter/sorted methods here that I used in Java, but I think this version is a bit more idiomatic. All the needed methods have been added directly to collections, so I don’t need to switch to streams first. That means I don’t need to switch back, either, so I need fewer steps. I also take advantage of the convention that property access (like offset or totalSeconds) is converted to the associated getter method (getOffset or getTotalSeconds) automatically.

That was fun, but if you really want see how crazy time zones can get, check out this figure, from the Wikipedia article on time zones in Antarctica.

antarctica_time_zones

If that doesn’t make a developer shudder, nothing will.

I decided to print those out, too. Here’s my Java version:

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class AntarcticaTimeZones {
    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        List<ZonedDateTime> antarticZones =
            ZoneId.getAvailableZoneIds().stream()
                .filter(id -> id.contains("Antarctica"))
                .map(id -> now.atZone(ZoneId.of(id)))
                .sorted(Comparator.comparingInt(zoneId ->
                        zoneId.getOffset().getTotalSeconds()))
                .collect(Collectors.toList());

        antarticZones.forEach(zdt ->
                System.out.printf("%s: %s%n", zdt.getOffset(), zdt.getZone()));
    }
}

This time I filtered on region IDs with the word “Antarctica” and I didn’t bother with the static import for Comparator.comparingInt. The result this time is:

-04:00: Antarctica/Palmer
-03:00: Antarctica/Rothera
+03:00: Antarctica/Syowa
+05:00: Antarctica/Mawson
+06:00: Antarctica/Vostok
+07:00: Antarctica/Davis
+08:00: Antarctica/Casey
+10:00: Antarctica/DumontDUrville
+11:00: Antarctica/Macquarie
+12:00: Antarctica/McMurdo
+12:00: Antarctica/South_Pole

Yeah, good luck with that. The Groovy version is naturally shorter:

import java.time.ZoneId

ZoneId.availableZoneIds
    .findAll { it ==~ /.*Antarctica.*/ }
    .collect { now.atZone(ZoneId.of(it)) }
    .sort { it.offset.totalSeconds }

In case you’re wondering, orbiting spacecraft experience many sunrises and sunsets in a 24 hour period, so timezones are hopeless. The International Space Station (according to the Wikipedia article on time zones in space) just gives up and uses GMT. The same article says that the “common practice for lunar missions is to use the Earth-based time zone of the launch site or mission control”.

Timekeeping on Mars gets worse, because the length of the Martian day is approximately 24 hours and 39 minutes, which is why Matt Damon kept referring to a sol.

That reminds me of this quote from Men in Black:

Jay: Zed, don’t you guys ever get any sleep around here?
Zed: The twins keep us on Centaurian time, standard thirty-seven hour day. Give it a few months. You’ll get used to it… or you’ll have a psychotic episode.

I suspect that if I spend much more time (ugh, again — see how hard it is to avoid those puns?) on this, I may be vulnerable to the same problem, so I’ll take this as a good time (haha — that one was intentional) to end.

Excluding Gradle Tasks with a Name Pattern

I’ve been spending a lot of time with Gradle build files in Android projects, which probably isn’t a big surprise given that I’m working on a book called Gradle Recipes for Android (coming soon to all your better ebook stores and (who knows?) maybe an actual, physical bookstore somewhere (but probably not), but you can always get it at O’Reilly or Amazon). In one chapter, I talk about excluding certain tasks in Gradle builds, and that led me to write an interesting custom task.

Gradle builds on Android have tons of tasks, and that number goes up and up when you add new build types or flavors. For example, on a trivial Android project, asking for the list of tasks gives:

> ./gradlew tasks
Starting a new Gradle Daemon for this build (subsequent builds will be faster).
Incremental java compilation is an incubating feature.
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Android tasks
-------------
androidDependencies - Displays the Android dependencies of the project.
signingReport - Displays the signing info for each variant.
sourceSets - Prints out all the source sets defined in this project.

Build tasks
-----------
assemble - Assembles all variants of all applications and secondary packages.
assembleAndroidTest - Assembles all the Test applications.
assembleDebug - Assembles all Debug builds.
assembleRelease - Assembles all Release builds.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
clean - Deletes the build directory.
compileDebugAndroidTestSources
compileDebugSources
compileDebugUnitTestSources
compileReleaseSources
compileReleaseUnitTestSources
mockableAndroidJar - Creates a version of android.jar that's suitable for unit tests.

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'HelloWorldAS'.
components - Displays the components produced by root project 'HelloWorldAS'. [incubating]
dependencies - Displays all dependencies declared in root project 'HelloWorldAS'.
dependencyInsight - Displays the insight into a specific dependency in root project 'HelloWorldAS'.
help - Displays a help message.
model - Displays the configuration model of root project 'HelloWorldAS'. [incubating]
projects - Displays the sub-projects of root project 'HelloWorldAS'.
properties - Displays the properties of root project 'HelloWorldAS'.
tasks - Displays the tasks runnable from root project 'HelloWorldAS' (some of the displayed tasks may belong to subprojects).

Install tasks
-------------
installDebug - Installs the Debug build.
installDebugAndroidTest - Installs the android (on device) tests for the Debug build.
uninstallAll - Uninstall all applications.
uninstallDebug - Uninstalls the Debug build.
uninstallDebugAndroidTest - Uninstalls the android (on device) tests for the Debug build.
uninstallRelease - Uninstalls the Release build.

Verification tasks
------------------
check - Runs all checks.
connectedAndroidTest - Installs and runs instrumentation tests for all flavors on connected devices.
connectedCheck - Runs all device checks on currently connected devices.
connectedDebugAndroidTest - Installs and runs the tests for debug on connected devices.
deviceAndroidTest - Installs and runs instrumentation tests using all Device Providers.
deviceCheck - Runs all device checks using Device Providers and Test Servers.
lint - Runs lint on all variants.
lintDebug - Runs lint on the Debug build.
lintRelease - Runs lint on the Release build.
test - Run unit tests for all variants.
testDebugUnitTest - Run unit tests for the debug build.
testReleaseUnitTest - Run unit tests for the release build.

Other tasks
-----------
jarDebugClasses
jarReleaseClasses
transformResourcesWithMergeJavaResForDebugUnitTest
transformResourcesWithMergeJavaResForReleaseUnitTest

To see all tasks and more detail, run gradlew tasks --all

To see more detail about a task, run gradlew help --task <task>

BUILD SUCCESSFUL

That’s about 50 tasks, and I haven’t added anything yet.

Android projects also include variants, which are combinations of build types and flavors. A build type describes whether you want to use debug or release configuration or define one of your own. Flavors allow you to build multiple similar applications that vary only in look and feel or minor code changes.

For example, in my giant Hello, World example (the same one I used in my O’Reilly video courses Learning Android and Practical Android), I use just the debug and release build types, but I have three flavors: arrogant, friendly, and obsequious.

Obsequious is such a good word. I rarely get to use it, though probably for good reason. If you’re trying to remember what it means, think Dobby from the Harry Potter novels:

Obsequious-Welcome

Under those circumstances, the number of tasks increases considerably:

> ./gradlew tasks
Incremental java compilation is an incubating feature.
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Android tasks
-------------
androidDependencies - Displays the Android dependencies of the project.
signingReport - Displays the signing info for each variant.
sourceSets - Prints out all the source sets defined in this project.

Build tasks
-----------
assemble - Assembles all variants of all applications and secondary packages.
assembleAndroidTest - Assembles all the Test applications.
assembleArrogant - Assembles all Arrogant builds.
assembleDebug - Assembles all Debug builds.
assembleFriendly - Assembles all Friendly builds.
assembleObsequious - Assembles all Obsequious builds.
assembleRelease - Assembles all Release builds.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
clean - Deletes the build directory.
compileArrogantDebugAndroidTestSources
compileArrogantDebugSources
compileArrogantDebugUnitTestSources
compileArrogantReleaseSources
compileArrogantReleaseUnitTestSources
compileFriendlyDebugAndroidTestSources
compileFriendlyDebugSources
compileFriendlyDebugUnitTestSources
compileFriendlyReleaseSources
compileFriendlyReleaseUnitTestSources
compileObsequiousDebugAndroidTestSources
compileObsequiousDebugSources
compileObsequiousDebugUnitTestSources
compileObsequiousReleaseSources
compileObsequiousReleaseUnitTestSources
mockableAndroidJar - Creates a version of android.jar that's suitable for unit tests.

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'HelloWorldAS'.
components - Displays the components produced by root project 'HelloWorldAS'. [incubating]
dependencies - Displays all dependencies declared in root project 'HelloWorldAS'.
dependencyInsight - Displays the insight into a specific dependency in root project 'HelloWorldAS'.
help - Displays a help message.
model - Displays the configuration model of root project 'HelloWorldAS'. [incubating]
projects - Displays the sub-projects of root project 'HelloWorldAS'.
properties - Displays the properties of root project 'HelloWorldAS'.
tasks - Displays the tasks runnable from root project 'HelloWorldAS' (some of the displayed tasks may belong to subprojects).

Install tasks
-------------
installArrogantDebug - Installs the DebugArrogant build.
installArrogantDebugAndroidTest - Installs the android (on device) tests for the ArrogantDebug build.
installFriendlyDebug - Installs the DebugFriendly build.
installFriendlyDebugAndroidTest - Installs the android (on device) tests for the FriendlyDebug build.
installObsequiousDebug - Installs the DebugObsequious build.
installObsequiousDebugAndroidTest - Installs the android (on device) tests for the ObsequiousDebug build.
uninstallAll - Uninstall all applications.
uninstallArrogantDebug - Uninstalls the DebugArrogant build.
uninstallArrogantDebugAndroidTest - Uninstalls the android (on device) tests for the ArrogantDebug build.
uninstallArrogantRelease - Uninstalls the ReleaseArrogant build.
uninstallFriendlyDebug - Uninstalls the DebugFriendly build.
uninstallFriendlyDebugAndroidTest - Uninstalls the android (on device) tests for the FriendlyDebug build.
uninstallFriendlyRelease - Uninstalls the ReleaseFriendly build.
uninstallObsequiousDebug - Uninstalls the DebugObsequious build.
uninstallObsequiousDebugAndroidTest - Uninstalls the android (on device) tests for the ObsequiousDebug build.
uninstallObsequiousRelease - Uninstalls the ReleaseObsequious build.

Verification tasks
------------------
check - Runs all checks.
connectedAndroidTest - Installs and runs instrumentation tests for all flavors on connected devices.
connectedArrogantDebugAndroidTest - Installs and runs the tests for arrogantDebug on connected devices.
connectedCheck - Runs all device checks on currently connected devices.
connectedFriendlyDebugAndroidTest - Installs and runs the tests for friendlyDebug on connected devices.
connectedObsequiousDebugAndroidTest - Installs and runs the tests for obsequiousDebug on connected devices.
deviceAndroidTest - Installs and runs instrumentation tests using all Device Providers.
deviceCheck - Runs all device checks using Device Providers and Test Servers.
lint - Runs lint on all variants.
lintArrogantDebug - Runs lint on the ArrogantDebug build.
lintArrogantRelease - Runs lint on the ArrogantRelease build.
lintFriendlyDebug - Runs lint on the FriendlyDebug build.
lintFriendlyRelease - Runs lint on the FriendlyRelease build.
lintObsequiousDebug - Runs lint on the ObsequiousDebug build.
lintObsequiousRelease - Runs lint on the ObsequiousRelease build.
test - Run unit tests for all variants.
testArrogantDebugUnitTest - Run unit tests for the arrogantDebug build.
testArrogantReleaseUnitTest - Run unit tests for the arrogantRelease build.
testFriendlyDebugUnitTest - Run unit tests for the friendlyDebug build.
testFriendlyReleaseUnitTest - Run unit tests for the friendlyRelease build.
testObsequiousDebugUnitTest - Run unit tests for the obsequiousDebug build.
testObsequiousReleaseUnitTest - Run unit tests for the obsequiousRelease build.

Other tasks
-----------
jarArrogantDebugClasses
jarArrogantReleaseClasses
jarFriendlyDebugClasses
jarFriendlyReleaseClasses
jarObsequiousDebugClasses
jarObsequiousReleaseClasses
transformResourcesWithMergeJavaResForArrogantDebugUnitTest
transformResourcesWithMergeJavaResForArrogantReleaseUnitTest
transformResourcesWithMergeJavaResForFriendlyDebugUnitTest
transformResourcesWithMergeJavaResForFriendlyReleaseUnitTest
transformResourcesWithMergeJavaResForObsequiousDebugUnitTest
transformResourcesWithMergeJavaResForObsequiousReleaseUnitTest

To see all tasks and more detail, run gradlew tasks --all

To see more detail about a task, run gradlew help --task

BUILD SUCCESSFUL

That’s just under 100, and the problem only gets worse if you add flavor dimensions. In the book, I add client flavors — one for Wayne Enterprises and one for Stark Industries. That gives me 3 x 2 = 6 different flavors and 2 build types, or 12 different variants, with all the (nearly 200) associated tasks. Whew.

Wayne-Help

Here’s a sample of the build file, just to show what this looks like:

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"
    defaultConfig {
        applicationId "com.kousenit.helloworld"
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        // no changes to debug type, so no need to list it here
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
               'proguard-rules.pro'
        }
    }

    flavorDimensions 'attitude', 'client'

    productFlavors {
        arrogant {
            dimension 'attitude'
            applicationId 'com.kousenit.helloworld.arrg'
        }
        friendly {
            dimension 'attitude'
            applicationId 'com.kousenit.helloworld.frnd'
        }
        obsequious {
            dimension 'attitude'
            applicationId 'com.kousenit.helloworld.obsq'
        }
        stark {
            dimension 'client'
        }
        wayne {
            dimension 'client'
        }
    }
}

Say I want to skip a task. For example, when I’m doing a regular build, I don’t always need to run the lint task, which gives interesting results but takes time. In Gradle, excluding a particular task from the build is as simple as using the -x flag.

That sounds good, but unfortunately there are many lint tasks:

> ./gradlew tasks | grep lint
lint - Runs lint on all variants.
lintArrogantStarkDebug - Runs lint on the ArrogantStarkDebug build.
lintArrogantStarkRelease - Runs lint on the ArrogantStarkRelease build.
lintArrogantWayneDebug - Runs lint on the ArrogantWayneDebug build.
lintArrogantWayneRelease - Runs lint on the ArrogantWayneRelease build.
lintFriendlyStarkDebug - Runs lint on the FriendlyStarkDebug build.
lintFriendlyStarkRelease - Runs lint on the FriendlyStarkRelease build.
lintFriendlyWayneDebug - Runs lint on the FriendlyWayneDebug build.
lintFriendlyWayneRelease - Runs lint on the FriendlyWayneRelease build.
lintObsequiousStarkDebug - Runs lint on the ObsequiousStarkDebug build.
lintObsequiousStarkRelease - Runs lint on the ObsequiousStarkRelease build.
lintObsequiousWayneDebug - Runs lint on the ObsequiousWayneDebug build.
lintObsequiousWayneRelease - Runs lint on the ObsequiousWayneRelease build.

Excluding lint leaves out some of them, but runs others.

> ./gradlew build -x lint | grep lint
:app:lintVitalArrogantStarkRelease
:app:lintVitalArrogantWayneRelease
:app:lintVitalFriendlyStarkRelease
:app:lintVitalFriendlyWayneRelease
:app:lintVitalObsequiousStarkRelease
:app:lintVitalObsequiousWayneRelease

I’m not sure what the “vital” part of those release tasks is, but I don’t want it. I suppose I could try excluding the tasks one by one, but that’s starting to feel like a lot of work.

Instead, I can add the following to the build.gradle file, which waits for the task graph to be assembled and then removes the undesired name pattern.

gradle.taskGraph.whenReady { graph ->
    graph.allTasks.findAll { it.name ==~ /lint.*/ }*.enabled = false
}

Gradle assembles a directed acyclic graph of tasks, available through the gradle object via its taskGraph property. By calling the whenReady method, I wait until that graph is assembled before modifying it.

The whenReady method takes a closure, whose argument is the graph. I retrieve all the tasks into a list, find all the tasks whose name matches the given regex (meaning the name starts with the letters lint), and disable them all.

> ./gradlew build | grep lint
:app:lintVitalArrogantStarkRelease SKIPPED
:app:lintVitalArrogantWayneRelease SKIPPED
:app:lintVitalFriendlyStarkRelease SKIPPED
:app:lintVitalFriendlyWayneRelease SKIPPED
:app:lintVitalObsequiousStarkRelease SKIPPED
:app:lintVitalObsequiousWayneRelease SKIPPED
:app:lint SKIPPED

This works, but it’s a permanent solution to a temporary problem. I’d rather make excluding those tasks optional. Fortunately, I can do that through a project property.

gradle.taskGraph.whenReady { graph ->
    if (project.hasProperty('noLint')) {
        graph.allTasks.findAll { it.name ==~ /lint.*/ }*.enabled = false
    }
}

Now I can exclude the lint tasks by specifying a -P flag on the command line:

> ./gradlew build -PnoLint | grep lint
:app:lintVitalArrogantStarkRelease SKIPPED
:app:lintVitalArrogantWayneRelease SKIPPED
:app:lintVitalFriendlyStarkRelease SKIPPED
:app:lintVitalFriendlyWayneRelease SKIPPED
:app:lintVitalObsequiousStarkRelease SKIPPED
:app:lintVitalObsequiousWayneRelease SKIPPED
:app:lint SKIPPED

This strikes me as a clean, elegant solution to the problem, but maybe only because I can’t think of anything easier. If you can, please let me know, because I turned in the complete draft of the stupid book this week (!!) and this is in one of the chapters. If you find an error or a better idea, there’s still (barely) enough time to update it and even give you credit (if not necessarily cashy money, though I will be happy to purchase for you the libation of your choice next time I see you).

Either way, the idea of manipulating the task graph inside the build file is a really useful one, so you shouldn’t exclude it (get it?).

Retrofitting Groovy

I’m teaching an Android development class this week, and one of our primary references is the book Android 6 for Programmers, 3rd edition, which was released last December. One of the examples in the book accesses the Open Weather Map RESTful web service and builds a UI around the results, which is pretty much the default Android developer app.

The app accesses Open Weather Map by creating an instance of the URL class, invoking openConnection on the result, and downloading the response using the resulting InputStream. It then parses the response using various classes in the org.json package, including JsonObject and JsonArray.

As you might imagine, this is a tedious way to solve the problem. Java is already verbose; adding Android makes it worse, and then doing networking and JSON parsing “by hand” is just too much. As a teaching example it’s fine, but I wouldn’t recommend that as a long-term solution.

For RESTful web services, I’ve been a fan of the Spring for Android project, which includes a class called RestTemplate that has a method called getForObject. Once you map a set of Java classes to the expected JSON response, accessing the web service becomes a simple one-liner. Much better.

The problem, however, is that the Spring for Android project is now dormant to the point of being inactive. The 1.0.1 release is dated December, 2012, and the 2.0.0 M3 milestone hasn’t changed in years. That makes me reluctant to keep recommending it to new Android developers.

Instead, the primary library for working with RESTful services in Android appears to be Retrofit, from Square. It’s very powerful and current, and the only problem is that the documentation is, shall we say, thin.

I wanted to show the students in my class how to rewrite the book app to use Retrofit instead of doing the low-level networking and JSON parsing. That meant I had to experiment with the library, which is something I’d been planning to do for years but never actually did. The good news is that Retrofit can be used in a stand-alone Java app, so I could try it out myself before worrying about the Android aspects of the problem.

As often happens, that lead me to Groovy. Most Groovy apps are combinations of both Groovy and Java, and I like to say that while Java is good for tools, libraries, and basic infrastructure, Groovy is good for everything else. While it’s unlikely I can convince my students to use Groovy in their apps (it’s a very conservative company), I could certainly use it myself during my learning process.

The book code eventually produced a Java class called Weather, used to hold formatted strings for the day of the week, the min and max temperatures forecasted for that day, the humidity percent, a String description of the weather, and a URL to an icon showing the weather (sunny, cloudy, or whatever). My goal was to use Retrofit to access the Open Weather Map API, download the resulting JSON response, convert it to classes, and then create an instance of Weather for each of the forecast days.

First I created a new Gradle-based project that allowed me to mix Java and Groovy together. Here’s the build file, showing the Retrofit dependencies.

apply plugin: 'groovy'

sourceCompatibility = 1.8

repositories {
    jcenter()
}

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.4.6'
    compile 'com.squareup.retrofit2:retrofit:2.0.1'
    compile 'com.squareup.retrofit2:converter-gson:2.0.1'

    testCompile 'junit:junit:4.12'
}

I’m using the Gson converter, which automatically converts the JSON response to a set of classes once I’ve defined them.

Step 1 in any mapping operation is to look at the form of the JSON response. Here’s an abbreviated sample, from http://api.openweathermap.org/data/2.5/forecast/daily?q=Marlborough,CT&units=imperial&cnt=16&APPID=d82ee6zzzzzzz .

{"city":{"id":4844078,"name":"Terramuggus","coord":{"lon":-72.47036,"lat":41.635101},"country":"US","population":0},"cod":"200","message":0.0156,"cnt":16,"list":[{"dt":1460044800,"temp":{"day":51.48,"min":49.69,"max":51.48,"night":49.69,"eve":51.48,"morn":51.48},"pressure":984.62,"humidity":97,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":9.98,"deg":170,"clouds":88,"rain":3.8}, { ... }, ... ]}

After the basic info, there is an array of 16 JSON objects representing the data I need, one for each day. (Note: to do this yourself, you’ll need to replace the APPID with your own, which you can get at the Open Weather Map site.)

Working top down, here is the set of POGOs (Plain Old Groovy Objects) I created to map to just the few parts I needed:

class Model {
    WeatherData[] list
}

class WeatherData {
    long dt
    TempData temp
    int humidity
    WeatherInfo[] weather
}

class TempData {
    double min
    double max
}

class WeatherInfo {
    String description
    String icon
}

To use Retrofit, I did what I normally do, which is to write a Groovy script and then eventually turn it into a class. That makes it easy to integrate with existing Java classes. Here’s the class I eventually created:

import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

class DownloadForecast {
    private static final String KEY = 'd82ee6...'

    private final Retrofit retrofit = new Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .baseUrl('http://api.openweathermap.org')
            .build()

    List<Weather> getWeatherList(String city='Marlborough', String state='CT') {
        OpenWeatherMap owm = retrofit.create(OpenWeatherMap)
        String address = "${URLEncoder.encode(city, 'UTF-8')},$state"
        Call<Model> model = owm.getData(q: address, units: 'imperial',
                cnt: '16', APPID: KEY)

        model.execute().body().list.collect { WeatherData wd ->
            Weather.parseData(wd)
        }
    }
}

I made both attributes private and final because I didn’t want Groovy to auto-generate and getters or setters for them. The instance of Retrofit is created using a builder, with its fluent syntax, in the recommended manner.

The getWeatherList method takes two strings representing the city and state. I gave both defaults (cool that you can do that in Groovy, isn’t it?), so I can invoke this method with zero, one, or two arguments, as the test cases will show.

The next requirement for Retrofit is that you provide an interface with the methods you want to invoke. In this case I called it OpenWeatherMap:

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.QueryMap;

import java.util.Map;

public interface OpenWeatherMap {
    @GET("data/2.5/forecast/daily")
    Call<Model> getData(@QueryMap Map<String, String> params);
}

While I could have written that in Groovy, in this case I provided it in Java, just to make the integration cleaner. The GET annotation shows that relative to the base URL I need to access the given path, and the QueryMap annotation is applied to a map of parameters used to form the resulting query string. The return type is a Call.

Returning to the getWeatherList method, I used the create method on retrofit to return an implementation of OpenWeatherMap. Then to make the actual call, I need to invoke the execute method using my map of parameters. Groovy makes that part particularly easy:

Call<Model> model = owm.getData(q: address, units: 'imperial', cnt: '16', APPID: KEY)

That uses the normal Groovy native syntax for maps. You’ll note that I URL encoded the city when assembling the address, using the normal (Java) URLEncoder class in the standard library.

Once I executed the call, I traversed to the list child element, based on the attribute name used in the JSON response. That gave me my collection of WeatherData objects.

Then I needed to map the WeatherData class to my desired Weather class, which I did through a static method called parseData in Weather.

import groovy.transform.ToString
import java.text.NumberFormat

@ToString
class Weather {
    final static NumberFormat numberFormat = NumberFormat.instance
    final static NumberFormat percentFormat = NumberFormat.percentInstance

    String day
    String min
    String max
    String humidity
    String description
    URL iconURL

    static Weather parseData(WeatherData data) {
        numberFormat.setMaximumFractionDigits(2)

        new Weather(day: new Date(data.dt * 1000).format('EEEE'),
            min: numberFormat.format(data.temp.min) + '\u00B0F',
            max: numberFormat.format(data.temp.max) + '\u00B0F',
            humidity: percentFormat.format(data.humidity / 100),
            description: data.weather[0].description,
            iconURL: "http://openweathermap.org/img/w/${data.weather[0].icon}.png".toURL()
        )
    }
}

That (almost) matches the Java Weather POJO in the book, which I populated from the WeatherData values. The last line in the getWeatherList method:

model.execute().body().list.collect { WeatherData wd ->
    Weather.parseData(wd)
}

converts the array of WeatherData objects into a collection of Weather objects and returns it.

To make sure this is working, here’s my test case:

import org.junit.Test;
import java.util.List;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.*;

public class DownloadForecastTest {
    private DownloadForecast df = new DownloadForecast();

    @Test  // default city,state is Marlborough,CT
    public void getWeatherList_MarlboroughCT() throws Exception {
        List<Weather> weatherList = df.getWeatherList();
        assertThat(16, equalTo(weatherList.size()));
        System.out.println("Today's weather: " + weatherList.get(0));
    }

    @Test // specify just city defaults to state of CT
    public void getWeatherList_NewLondonCT() throws Exception {
        List<Weather> weatherList = df.getWeatherList("New London");
        assertThat(16, equalTo(weatherList.size()));
        System.out.println("Today's weather: " + weatherList.get(0));
    }

    @Test // the weather has got to be better in Honolulu
    public void getWeatherList_HonoluluHI() throws Exception {
        List<Weather> weatherList = df.getWeatherList("Honolulu", "HI");
        assertThat(16, equalTo(weatherList.size()));
        System.out.println("Today's weather: " + weatherList.get(0));    }
}

I used Java to write the test, mostly to demonstrate that I can access the Groovy classes from Java without any issues. All I’m testing is that I get 16 Weather objects in the results, as I expected (because of the supplied value of the cnt parameter). The printed output shows today’s weather in each location.

Today's weather: Weather(Thursday, 77.38°F, 79.36°F, 97%, scattered clouds, http://openweathermap.org/img/w/03n.png)
Today's weather: Weather(Thursday, 46.44°F, 48°F, 90%, moderate rain, http://openweathermap.org/img/w/10d.png)
Today's weather: Weather(Thursday, 49.69°F, 51.48°F, 97%, moderate rain, http://openweathermap.org/img/w/10d.png)

The first result is for Honolulu; the other two are in Connecticut. In other words, April hasn’t really made it’s way to Connecticut yet.

Now that the system is working, the next step would be to port everything to Java and add it to the Android app, making the REST call in an AsyncTask and so on. After coding in Groovy, however, the idea of porting all that easy code back into Java is just depressing, so I decided to blog about it instead.

A Groovy approach to npm-gate

Recently the JavaScript community experienced a serious disruption when a developer removed one of his deployed libraries from the central npm server, an event now being referred to as npm-gate. I don’t want to get into the various ethical, moral, or legal issues about that here. Rather, I want to show how trivially the missing functionality can be supplied using Groovy.

The chaos came from a function known as left-pad. All the function does is take a string, a number, and a delimiter, and returns a padded string of the requested length using the supplied delimiter. Here are the examples shown on the home page:

leftpad = require('left-pad')

leftpad('foo', 5)
// => "  foo" 

leftpad('foobar', 6)
// => "foobar" 

leftpad(1, 2, 0)
// => "01"

As you can see, there’s not much to it. The implementation is pretty simple as well:

module.exports = leftpad;

function leftpad (str, len, ch) {
  str = String(str);

  var i = -1;

  if (!ch &amp;&amp; ch !== 0) ch = ' ';

  len = len - str.length;

  while (++i &lt; len) {
    str = ch + str;
  }

  return str;
}

The Groovy implementation is almost trivially easy, because the Groovy JDK already has a method in the String class called padLeft.

assert 'foo'.padLeft(5)    == '  foo'
assert 'foobar'.padLeft(6) == 'foobar'
assert '1'.padLeft(2, '0') == '01'

It’s easy enough to make a method out of this:

String leftPad(s, len, ch=' ') {
    s.toString().padLeft(len, ch.toString())
}

assert '  foo'  == leftPad('foo', 5)
assert 'foobar' == leftPad('foobar', 6)
assert '01'     == leftPad(1, 2, 0)
assert ' null'  == leftPad(null, 5)

So far, so good, plus it’s also a nice example of specifying a default parameter in a method.

Of course, providing a function like that to JavaScript developers doesn’t really help, because they can’t invoke it (easily) from JS. Might as well make it a RESTful web service, then. I made a Ratpack app and added a ratpack.groovy script:

import static ratpack.groovy.Groovy.ratpack

ratpack {
    handlers {
        get() {
            String s = request.queryParams.string ?: 'hello'
            String len = request.queryParams.num ?: '5'
            String delim = request.queryParams.delim ?: ' '
            response.send s.padLeft(len.toInteger(), delim)
        }
    }
}

All the query parameters are strings by default, but I wanted to make sure they all had values. Thus the series of Elvis operators to provide defaults. Next I went through the simple series of hoops necessary to deploy the app to Heroku, so I can access it using HTTP:

> http leftpad.herokuapp.com

hello

> http leftpad.herokuapp.com string==foo num==5

  foo

> http leftpad.herokuapp.com string==foobar num==6
foobar

> http leftpad.herokuapp.com string==1 num==2 delim==0

01

To make the HTTP requests, I’m using httpie, which is my standard curl replacement. Feel
You can use curl, or just type a URL like

http://leftpad.herokuapp.com/?string=foo&num=8&delim=x

into a browser to see the results.
Normally at this point I would make some kind of joke lamenting how so many JavaScript developers needed an online, downloaded dependency just to pad a string, but I won’t. After all, coding in JavaScript is its own punishment. I’ll just note that, yet again, Groovy made something trivial that apparently other languages have to work to do.

The Shadow Knows Gradle

Someone recently complained on Twitter that the so-called Shadow plugin for Gradle, written by the inestimable John Engleman, no longer worked on Gradle 2.11 or 2.12. Commenters were quick to point out that the latest version (1.2.3) of the plugin did work. I thought I’d put together this quick blog post to demonstrate that it works fine.

As a overview, the Shadow plugin creates what’s called a “fat” jar, including all the dependencies, so you can deliver a runnable final product without installing anything other than Java.

I made a trivial Java library project using the Gradle Init plugin, using Spock testing (because I couldn’t help using Spock). Fortunately, this time I remembered to create a folder to hold it in ahead of time.

> md temp/shadow_demo
> cd temp/shadow_demo
> gradle init --type java-library --test-framework spock

That created a typical Java project, with folders src/main/java and src/test/groovy in it, along with a source class called Library.java and a test called LibraryTest.groovy.

(First minor complaint: the test case should be called LibrarySpec.groovy. The compiler doesn’t require you to end your Spock tests with the word Spec, but it’s a standard idiom by now. Plus, it helps me keep straight which of my tests are Spock tests and which are JUnit tests.)

I then opened the build.gradle file in the root of the project in order to add the shadow plugin. Here’s how it looked when I started:

/*
 * This build file was auto generated by running the Gradle 'init' task
 * by 'kousen' at '3/14/16 5:20 PM' with Gradle 2.12
 *
 * This generated file contains a sample Java project to get you started.
 * For more details take a look at the Java Quickstart chapter in the Gradle
 * user guide available at https://docs.gradle.org/2.12/userguide/tutorial_java_projects.html
 */

// Apply the java plugin to add support for Java
apply plugin: 'java'

// Apply the groovy plugin to also add support for Groovy (needed for Spock)
apply plugin: 'groovy'

// In this section you declare where to find the dependencies of your project
repositories {
    // Use 'jcenter' for resolving your dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
}

// In this section you declare the dependencies for your production and test code
dependencies {
    // The production code uses the SLF4J logging API at compile time
    compile 'org.slf4j:slf4j-api:1.7.18'

    // We use the latest groovy 2.x version for Spock testing
    compile 'org.codehaus.groovy:groovy-all:2.4.6'

    // Use the awesome Spock testing and specification framework even with Java
    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
    testCompile 'junit:junit:4.12'
}

(Second minor complaint: the build file adds both the java and groovy plugins. That’s redundant. The groovy plugin already includes all the needed java functionality.

I removed the comments and cleaned up the build file a bit, adding the shadow plugin.

plugins {
    id 'groovy'
    id 'com.github.johnrengelman.shadow' version '1.2.3'
}

repositories {
    jcenter()
}

dependencies {
    compile 'org.slf4j:slf4j-api:1.7.18'

    compile 'org.codehaus.groovy:groovy-all:2.4.6'

    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
    testCompile 'junit:junit:4.12'
}

I’m using the newer plugins block syntax instead of the conventional `buildscript` approach. The page at the Gradle plugins site for the Shadow plugin (https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow) shows the details. Note that the section at the bottom of that page labelled “About the new plugin mechanism…” gives additional details on the newer approach.

If you run the gradle tasks command at this point, you’ll see a lot of output, some of which is relevant for this demo.

> gradle tasks
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles test classes.

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Documentation tasks
-------------------
groovydoc - Generates Groovydoc API documentation for the main source code.
javadoc - Generates Javadoc API documentation for the main source code.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'shadow_demo'.
components - Displays the components produced by root project 'shadow_demo'. [incubating]
dependencies - Displays all dependencies declared in root project 'shadow_demo'.
dependencyInsight - Displays the insight into a specific dependency in root project 'shadow_demo'.
help - Displays a help message.
model - Displays the configuration model of root project 'shadow_demo'. [incubating]
projects - Displays the sub-projects of root project 'shadow_demo'.
properties - Displays the properties of root project 'shadow_demo'.
tasks - Displays the tasks runnable from root project 'shadow_demo'.

Shadow tasks
------------
knows - Do you know who knows?
shadowJar - Create a combined JAR of project and runtime dependencies

Verification tasks
------------------
check - Runs all checks.
test - Runs the unit tests.

Rules
-----
Pattern: clean<TaskName>: Cleans the output files of a task.
Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.

To see all tasks and more detail, run gradle tasks --all

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 9.466 secs

Note the existence of the shadowJar task, which creates the fat jar.

My trivial app doesn’t have a Java class with a main method in it, however, so creating a shadow jar wouldn’t actually accomplish much. Therefore, after renaming src/main/java to src/main/groovy, I added a tiny groovy script called src/main/groovy/demo.groovy:

println 'What up, World?'

Then I added the application plugin to the Gradle build and set the main class to my script, giving me the final form of the build file:

plugins {
    id 'groovy'
    id 'application'
    id 'com.github.johnrengelman.shadow' version '1.2.3'
}

mainClassName = 'demo'

repositories {
    jcenter()
}

dependencies {
    compile 'org.slf4j:slf4j-api:1.7.18'
    compile 'org.codehaus.groovy:groovy-all:2.4.6'

    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
    testCompile 'junit:junit:4.12'
}

The (built in) application plugin adds the run and shadowRun tasks to Gradle. Executing those is easy enough, too:

> gradle run
:compileJava UP-TO-DATE
:compileGroovy
:processResources UP-TO-DATE
:classes
:run
What up, World?

BUILD SUCCESSFUL

Total time: 2.688 secs

> gradle runShadow
:compileJava UP-TO-DATE
:compileGroovy UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:shadowJar
:startShadowScripts
:installShadowApp
:runShadow
What up, World?

BUILD SUCCESSFUL

Total time: 2.503 secs

The first runs the app without the shadow jar, and the second uses it. Everything looks good. The GitHub page for the shadow plugin describes lots of ways to customize it, but I didn’t need any of that for my demo.

Of course, there’s no way you can talk about the shadow plugin without running the additional task it adds, namely knows:

> gradle knows
:knows

No, The Shadow Knows....

                                        .
                                .MMMMMO      .M
                              .MMMMMMMMM. MMMM.
                              .MMMMMMMMMMMMMMM.
                               .MMMMMMMMMMMMM
                               .MMMMMMMMMMMM
                            .+MMMMMMMMM,ZMMM.
                          ...7MM8D8MM.ZMMMMM.
       ..                      MMZ..MZZNMMMMM
      ....                  MMMMMMMZZZ.MMMMMMOOOOOO..
      ...                7MMMMMMMMMZZZMIMMMMOOOOOMMMM..
      .. .~.                .MMMMMOMZZZMZMMOOOOOMMMM MM.
         .MMMMM             ..MMM.7DOMOMOOOOOOOMM MMMMM Z
      ..  MMMMMMM..  .     ...MMMMMMMMMOOOOOOMMMMMMMMMM
       .    .MMMMMMM.       .MMMMM MMMMMOMOMMMMMMMMMMM
             MMMMMMMMM    .MMM.MMMMMMMMMMMOMMMMMMMMMMM
             .MMMMMMMM   $MMMM MMMMMMMMMMMMMMMMMM MMM
              MMMMMMNMMMMMMMM M.MMMMMM.MMMMMMMM MMMMMM
             ..MMMMMMMMMMMMMMMMMMMMMMMMMMMM.MNMMMMMMM .
              ...MMMMMMMMMMM MMMMMMMMMMMMM.MMMMMMMM.
                 MMMMMMMMMM.MMMMMMMMMMMMMDMMMMMMMM.
                ..MMMMMMMMMMMMMMMMMMM M,MMMMMMMMMMMMMMMZMMMMM  +D            ,
                     .:DMM.M. MMMMMMM.MMMMMMMMMMMMMMI:MMMMM      :MMO
                        . MMMMMMMMMMMMMMMMMMMM.MMMMM8   NMMMN
                       ..MMMMMMMMMMMMMMMMMMMMM  MMMMN.
                       .MMMMMMMMMMMMMMMM. MMM7  ,      . =.
                       MMMMMMMMMMMM.$MM  M   .   MM7
                      MMMMMMMMM=MI:M8  . MNOM     M
                     MMMMMMMMMM.      .
                    MMMMMM .
                   +MM



BUILD SUCCESSFUL

Total time: 1.043 secs

That, of course, is worth the whole demo.:)

Rough cut of Gradle Recipes for Android now available

My latest book, Gradle Recipes for Android, is now available as a “Rough Cut” at O’Reilly. You can get it at http://shop.oreilly.com/product/0636920032656.do.

gradle-recipes-for-android

Rough cuts are preliminary versions of O’Reilly books, which are released while a book is still in progress, without special effort taken for formatting or anything else.

In this case, however, the rough cut is pretty close to the final version. Recipe books at O’Reilly are like their cookbooks, only shorter. My book has about 27 recipes, which are short discussions of how to do specific tasks, in this case involving the Gradle build tool with the Android plugin, used to build Android applications. The book also contains information about how to use the only officially supported IDE, Android Studio.

The O’Reilly authoring system supports Asciidoc, which is wonderful. I found it much, much easier to write my book using Asciidoc, commit it to their git repository, and then generate the resulting pdf afterwards. The book is available in Safari Books Online, too, which show it formatted for HTML. The results are very nice, and allow you to copy and paste code from the browser to your editor of choice. For the record, I wrote the book using the Atom editor from GitHub, which has a nice Asciidoc preview mode.

The book is basically finished. I’m doing some editing and considering adding a recipe or two. If you have any suggestions, however, please feel free to send them along. I hope to complete the book in the next week or so, so it will be available in print form just in time for the holidays.:)

I’ve also been doing a lot of video course recording for O’Reilly over the past few months. So far the courses available are:

All the courses are available for sale, or on Safari. The last one (Gradle for Android) is similar to the book, but the book has more depth, a different approach, and lots of reference information. The book has appendices on Groovy and Gradle, while the video summarizes both topics.

I should mention in the midst of all this shameless self-promotion that there is also a Packt book called Gradle for Android by Kevin Pelgrims. Packt books vary wildly in quality, but Kevin’s book is excellent. In fact, it was so good, that (combined with the fact I’d already coincidently recorded a video course of the same name) I decided I needed to switch my book to the recipe style. His book is much more the traditional exposition, with chapters and depth. Mine is more the “here’s a problem, now here’s a solution” style. I think both books are complementary. At least I hope so.

On an unrelated note, I now have a revised home page, http://www.kousenit.com. It’s about time.

I hope you enjoy the book and/or video courses, or even just my new home page. As always, any errors or omissions are entirely my responsibility. I’m just happy to be making all this content available. Now I have to finish this weekend’s No Fluff, Just Stuff conference in Boston, travel to Raleigh for a Groovy course, and then get in line to see The Martian.

My father, at the end

Back May of 2010, my mother called to let me know that my father was going into the hospital for minor surgery. There was nothing to worry about, and no reason for me to drive all the way there just to see him, especially since he was only going to be in the hospital for about a day.

My contrary nature, combined with my constant desire to exceed expectations, combined to make me seriously consider driving there anyway. I lived in eastern Connecticut, and my parents lived in York, PA, which is about a 5 1/2 hour drive, so it wasn’t a trivial undertaking. As it happened, this was a Sunday and I didn’t have a class the following week, so after talking to Ginger I decided to just go.

I called as I got close, and they told me the surgery had been a success and he was fine, but they had received some extra news, so I should just go to the hospital.

Between the time my mother originally called and when I got to the hospital, the doctors had run some tests, and they weren’t good. It took some time to confirm the results, but the diagnosis was clear — stage 4, grade 4, cancer. They gave him three to six months to live.

Before this story gets unnecessarily maudlin, let me mention a few things. It’s been over five years and he’s still here, though he’s fading away by the moment.

When he first told me the results, a look passed between us that was the culmination of all the years that had come before it. He understood what was happening and already accepted it. I understood that and was willing to go along with whatever he wanted. Even more, we both knew this wasn’t an emergency. It wasn’t like we had massive differences to resolve. We’d already been through all that. Everything that needed to be said had already been said, and we were fine now. All that was left was to make plans and enjoy each other’s company until the end.

As we get older, we re-negotiate our relationships with our parents over and over again. We go through stages in life, from child to adult to possibly parents of our own, while retaining our ability to drive them crazy. They transition from supreme authority to advisor to friend and even dependent, while still retaining their ability to drive us crazy. We change, and they change, and the relationship has to change as well, which all too often isn’t easy.

I returned to my home, of course. Those of you who have been through cancer, either personally or with a relative, know one of its most frustrating aspects is that everything happens in slow motion. You try a new therapy and wait before you can find out if it’s working. You run tests and wait until they show anything. You watch the quality of life deteriorate slowly, with occasional interruptions of good health and progress, before the inevitable sets in again. It can be a long drawn out process, except when it isn’t, which I imagine may be worse. I don’t know. I’ve only seen it this way.

Over the years, we got in the habit of visiting them twice a year, over the 4th of July and for Thanksgiving, and they normally came to us once during the winter, with maybe another visit if it was convenient for some reason. The initial diagnosis was in May, so the next time I saw him was in July. He took me aside and showed me what he’d done in the meantime.

First, he’d taken this picture of himself.

Morton Kousen, MD

Morton Kousen, July 2010

It’s a good picture, which wasn’t terribly surprising. He’d discovered photography as a late-life obsession, and showed remarkable talent at it.

He’d also written his own obituary for the newspaper, which sounds a bit morbid but was in fact entirely natural for him. His reasons for both the picture and the obit were:

  • Given the way the disease normally progressed, he didn’t think he’d ever look this good again
  • He wanted to be prepared, at all times, for everything
  • When he finally died, he didn’t want to leave those details for my mother to deal with

That’s my dad all over. He always had a plan and he never wanted to be less than perfectly prepared for whatever came, and he cared for my mother more than anyone in the world.

My father was born on August 2, 1938, near Washington, DC, the eldest of two sons of immigrants from Eastern Europe. He never liked the name Morton, so he went by Mort, which wasn’t much better. His parents didn’t give him a middle name at all, he didn’t have that as an alternative. My mother, Bonnie, was born in 1941 in a different part of Washington, DC. She was the eldest daughter of first generation US citizens. They married when she was only 19 and he was only 22, which is younger than my son is now.

He graduated from George Washington University with a degree in Pharmacy, which he mostly hated. He then enlisted in the Air Force and received his commission as a second lieutenant. He wasn’t a flyer — he worked in a pressure chamber, eventually getting an M.S. in Physiology at the University of Illinois on his way to becoming a captain.

Talking to him later revealed a lot about his life then. Once, early on, he was placed in command of a squadron. As a raw 22 year old he had no idea how to handle them. What he did have, however, was a very experienced master sergeant.

He called the sergeant into his office and said to him, “I’m in command and I can’t look like I’m not, but I know you have a lot more experience than I do. So I tell you what: you give me suggestions privately and help me out, and I’ll take everything you say seriously and back you up wherever you need it. At the end of the year, I promise you this. Whatever rating I get is the same rating you’ll get. If I do well, so will you. If not…”

Doesn’t that sound like management advice for the ages? It still makes me smile.

Needless to say, they got along well and both did well. My father was transferred several times (such is life in the service). The only parts I remember where moving from Champaign, IL (where he got his master’s) to Peru, IN and then to Del Rio, TX.

That last move wasn’t a happy one. He was hoping to go on and get a Ph.D. He was already accepted into a program in Amsterdam and was excited about relocating to Europe, though it wasn’t a sure thing. Instead, at the last minute, he was called into his commanding officer’s office.

“Instead of going to Europe, we’d rather you get promoted to Captain and move to Del Rio, TX. How would you like that?” the officer said.

“I wouldn’t like that at all, sir,” my dad replied.

Nevertheless, that’s where we went. When my father told me the story, I remember saying wistfully I wished it had gone the other way. I would have loved to have grown up in Amsterdam while he was working on an advanced degree. He just rolled his eyes and said, “sorry.”

While in Texas, my father became friends with an obstetrician. Like all Jewish boys of his generation, my father had always been interested in becoming a doctor. His friend offered to take him to watch a delivery, which he did.

(I’ve always found that rather remarkable. Were unqualified friends always allowed to do that? Did they get the mother’s permission? Was the fact that my father was an officer make any difference?)

The delivery was easy and successful and everyone went home happy.

“Is that what made you want to go to medical school?” I asked.

“No,” he said. “I thought it was boring.”

He told me his friend offered to take him to one more delivery. This time there were complications, and an emergency C-section, and lots of things happening all at once. Still, the result was a happy, healthy baby at the end.

My father nodded to me. “That’s when I decided to become a doctor,” he said.

The military isn’t terribly flexible, however. They weren’t willing to send him, because he was thirty years old at the time.

My father applied to several medical schools anyway. He received a few interviews, including one at the University of Maryland.

When he went, the interviewer said, “why should we accept you over a 22 year old with your qualifications?”

“I don’t think you’ll find a 22 year old with my qualifications,” my father replied.

They accepted him. Then he went back to the military and told them he was in, so they should send him anyway. They said they could do that, but that the program resulted in a commission as second lieutenant. My father replied that he was already a captain.

“Make it lieutenant colonel and we’ll talk,” he said.

That wasn’t going to happen. They offered him a promotion to major instead, probably to forget the whole thing.

He retired from the Air Force after 12 years of service, and, as we used to say, we all went to medical school. My mother got a job as a teller in a bank, then as a secretary at a police station. My father studied during the day and worked as a pharmacist in the evenings. On the weekends, my mother used to take my brother, sister, and I scrounging around the neighborhood for bottles to return so that we could take the change and get ice cream from the Good Humor truck. We didn’t have much money, but we didn’t feel particularly poor, either. We also knew it was temporary, and that we were all in this together. My allergies and asthma didn’t help, but they didn’t matter, either.

On nights when my mother had to work late, my job was to take care of my brother and sister. I’d warm up the Spaghetti-O’s, make sure they started their homework, and sometimes put them to bed.

I was seven years old at the time. We’d never heard of “latch-key” kids back then. It took a few years of therapy before I even mentioned it, because I hadn’t realized it was important. I just can’t believe how lucky we were that nothing seriously went wrong, though my sister tells me lots of things were happening I had no idea about, which probably isn’t a big surprise.

After medical school, we moved to York, PA (this was 1974, I believe), where my parents lived ever since. My father loved, loved, loved being a doctor. He must have delivered tens of thousands of babies in the York area over the years, and while he got exhausted, he never minded coming in during the dead of winter to deliver another one. Since he was the Jewish partner in his group, he always worked on Christmas and delivered all the babies while wearing a Santa suit.

He used to tell me how lucky he felt that he was being paid to do a job that he would willingly do for nothing, and always hoped I’d find one too. It took me about 40 years before I could say the same. Heck, we’re all career changers in my family. My mother went back to school in York and became a nurse. My sister started off as a business analyst and later became a physical therapist (or physical terrorist, as she liked to call it). In fact, she just got her DPT (doctor of physical therapy). Now she’s Dr. Doll, which just adorable. (Just kidding, kiddo. :))

My father could obsess like nobody I ever met. He would pick a topic, or an issue, or even just where to buy a piece of hardware, and beat it into the ground. He’d read every review, talk to every person he knew about it, and check out every detail before making a purchase. It was, frankly, exhausting for those of us around him. The keys were “optimize” and “maximize”. The thought that he hadn’t done the necessary work to prepare was an anathema to him. He couldn’t bear it.

They say your greatest strength is often also your greatest weakness. In all my life, I’ve never met anyone who wanted to know more than my father. He felt this burning need to know what was next, in any topic, all the time.

It’s a great asset, but it could be very hard on the people around him, especially when I was a young boy who idolized him and desperately wanted his approval. Any time I accomplished anything, he would want to know what’s next. “And then what?” was practically a mantra. There was never any time to celebrate. I had to correct any deficiencies and move on. Plus, he would tell me that he always wished he’d been born with my natural ability to learn, which he thought was a compliment, but came across as a dangerous form of jealousy.

Particularly bad times would be when I’d stop working, for a variety of mental and emotional reasons. None of that made any sense to him. The thought that I was wasting my talent drove him up a wall. It took me literally years to come to understand that his pushing me to go further wasn’t really about me at all — it was how he drove himself, all the time. The inadequacies I felt at never being good enough for him were a child’s perception of an adult’s emotion that was never really directed at me anyway.

Once I was able to relax a bit and not worry so much about being perfect all the time, I could look for his approval without fear. To my considerable astonishment, I discovered I’d had it all along. Frankly, he never understood my confusion.

I tried to make a list of his “obsessions”, more hobbies really, over time. He attacked them with a passion normally reserved for military invasions:

  • Chess. An early one, mostly before my time, but resulting in lots and lots of chess books around the house. I improved until I became his rough equal in high school and then leveled off. His style was to get bored and attack, just to see what would happen. My need to find the “right” move in every situation made those games tough, but we somehow managed to have fun anyway. About a year ago he was playing against my sister’s daughter and we finally decided to play again. That’s when I knew how old and sick he’d become — the fire was gone. He was almost defeated before we started. I hated it. We never played again.
  • Radio controlled model airplanes. We lost our ping-pong table to that. He’d spend hours building them, then he’d fly them until they crashed, and start a new one. I can only imagine what he would do with today’s drones.
  • Sailing. After a very brief experiment with a small power boat, he decided he wanted to learn to sail. He read everything imaginable, bugged all his friends about it, and over the years went through a series of increasingly large sail boats. I never liked sailing (even less with his persistent intensity around), so that was inflicted much more on my sister than me. My mother hated sailing more than I did, but of course she went with him until he became too old to do it any more.
  • Photography. Like everything else, he read tons of books, talked to experts, and attended courses on it. At that point he would need friends to transport him to the destinations, because he actually couldn’t sit that long anymore, much less drive. Still, he was extraordinarily good. One of his photos actually won the Grand Prize at the York Fair one year, and several others were sold at charity auctions. (I’d show them here, but I don’t have them locally. I’ll do that later.)

I’m sure there were more, but that will give you an idea. The obsessiveness came out even on little things, though. One day I was in his kitchen, reading something, when he wanted to know about a computer problem.

Everyone in I.T. becomes the system administrator for their families, whether they want to or not. That has the unfortunate side effect of forcing you to deal with computer systems so horrible you normally wouldn’t touch them with a ten foot pole. In my family, that added another layer to the family joys of Thanksgiving, knowing that my father would have a list of accumulated problems he’d want me to solve, “but no pressure”. Yeah, right.

One day I was sitting in the dining area reading something. I don’t remember what it was. It could have been as silly as catching up on my Twitter feed, but I was wrapped up in it and didn’t to leave.

“I’ve got this software that isn’t saving properly,” he said. “Maybe you could take a look at it.”

“Sure, when I’m finished this.” I replied.

“Okay.”

But it wasn’t okay, of course. He sat there for maybe five minutes, and then went over to the computer, opened that software, and proceeded to read all the menu items, one by one, out loud, as if daring me to ignore him.

I was older, though. I acted like I didn’t hear him, at least for a minute. Then I heard one menu option in particular.

“That could be it,” I said, angry for responding but unable to help myself.

“I’ll go check the one in my office,” he said.

“Give me about ten minutes and I’ll come in and help.”

I managed to stall for most of ten minutes, but eventually I had to go. He was in the process of his third reboot, with the Windows 98 (shudder) disks in hand because of problems. In fact, after talking to him, I couldn’t believe he hadn’t destroyed his entire computer. Somehow we got it working though, even if we never quite fixed the original problem.

My father faced his illness with an upbeat and positive attitude that I found amazing. It never got the best of him, by which I mean cancer might have ravaged his body, but it never claimed who he truly was. Even more, he could rally for short periods of time when he felt it necessary. Here’s my favorite relatively recent picture of both of us, taken at my niece Jessica’s Bat Mitzvah in April, 2013.

dad_and_me

One last story. When my father was diagnosed, I had just begun work on my technical book, Making Java Groovy. I’d actually been working on it for a couple of years, but my original publisher cancelled it. I’d moped around for a month or so, and then managed to get it accepted at Manning. I’d just started at Manning the previous March.

My goal was to somehow finish the book in time for him to have a copy. The diagnosis in May of 2010 made that look unlikely. He was just hoping to survive into November so he could make his 50th wedding anniversary.

(He made that, of course. He’ll just miss his 55th.)

The initial attempts at radiation didn’t really help, so things were looking bad. Then they switched to a particular chemotherapy drug, and he improved almost immediately. That bought me time. Heck, it bought all of us time.

I hadn’t realized that by moving to Manning I was going to have to re-write the book almost from scratch, but that’s what happened. The book took me years to write, and was eventually published in September of 2013. My plan all along was to get a picture of him holding the book, but for reasons I can’t begin to fathom, I never took that picture. I made sure he got the first copy off the presses, and he showed it to his friends, and celebrated it with me, but for some bizarre reason it never made it into a photograph.

Like everything else, though, it didn’t matter. I knew he had it, and he enjoyed having it, and that’s enough. The most astonishing part, to him, was the dedication. Here it is, in full:

To my father, Morton Kousen, MD,

who taught me everything I know about dedication,

persistence, and facing the future with a positive and

upbeat attitude, despite whatever pain and difficulties lay ahead.

He will always be my best example of what a man should be.

I wrote it while I was at his house, as part of the last steps of preparing the book for publication. That meant I had to write it and then not tell him about it for almost three months until the physical book arrived. He was blown away by it, which of course was the idea. I still don’t think I got it quite right. It still comes across as a bit awkward to me. But it was, and is, good enough.

He’s still not quite gone. Last weekend he went into the hospital and nobody expected him to come out again. In fact, the ambulance drivers last Sunday didn’t want to bring him home because they didn’t think he’d survive the trip. He’s been lingering at home, not accepting any food or water, for eight days now. I didn’t know that was even physically possible. At least he’s got enough morphine in him to stop a charging rhinoceros, so I don’t think he’s in pain any more. We’re just waiting for the end.

When he was first diagnosed, he told me my job was to give the eulogy. He felt it was important to him professionally to keep what he was actually like to himself, because he didn’t want it to interfere with his job, but when it was over he wanted people to know the truth. As a result, I’ve been thinking about this for over five years now, off and on, and this blog post is part of the result. I honestly thought I would have given it by now. I also feel that even though I’ve given you some idea of who he is, I’ve left so much out. He always put on such a bold, commanding front, but underneath he was such an incredibly gentle man. He could, and did, talk to anybody, and he did so with decency and respect regardless of position or what they could do for him. He also had very little patience, and a strong temper, and made astonishing social blunders.

In the end, though, he’s my dad. He did his best, and he cared about all of us deeply, and I’m very grateful for that. When he was first diagnosed, we exchanged a look, and I think we both saw this day, and it was good enough.

Goodbye, dad.

Follow

Get every new post delivered to your Inbox.

Join 2,097 other followers

%d bloggers like this: