Friday, January 31, 2020

Gson can create Kotlin objects with surprisingly null parameters

One of my projects uses Gson to load a bit of state from a JSON file. The state is represented by a Kotlin data class with non-null properties. I expected that instances of this class would fail to initialize if Gson didn't find a JSON property for all the properties, in which case the program would fall back to my default. Instead it created an instance with that non-null-declared property set to null, which produced a confusing stack trace. Checking the property against null works, of course, but Kotlin produces a "senseless comparison" warning.

Saturday, January 25, 2020

IntelliJ "apply patch" doesn't use Git

The class I'm on staff for historically distributed updates to its assignment as patches generated by git diff. Students would apply them to their projects with the VCS | Apply Patch feature of IntelliJ (Android Studio, in our case). This appeared to work pretty well, but eventually I noticed a case where not all changes from the patch were actually applied to the project. Apparently the "apply patch" feature is not powered by Git, even in Git diffs for Git projects. Using git apply from the Terminal tab works better.

Tuesday, January 21, 2020

Using Weka in Android

I tried to build an Android app involving the Weka machine learning library, but adding Weka to the project makes the build fail with a ton of duplicate class errors. For some reason, Weka seems to have two very similar dependencies that both get selected for the Android packaging process. One of them can be removed with a bit of extra Gradle configuration (Kotlin shown):
implementation("nz.ac.waikato.cms.weka:weka-stable:3.8.4") {
    exclude(module = "java-cup-runtime")
}

Sunday, January 19, 2020

Android Gradle 3.5.3 doesn't clean the desugared local JARs directory

One of my Gradle plugins helps swap out a handful of JAR dependencies in Android projects. I noticed that after using it for a while on an Android Gradle 3.5.3 project, building an APK would eventually start failing with a "program type already present" error.

I determined that the problem occurred when two copies of the same JAR dependency ended up in the external_file_lib_dex_archives intermediates directory. Files there are named with a combination of a number (increasing within one run) and the original filename. I observed the same JAR with different numbers, presumably from different builds. That directory is populated by DexFileDependenciesTask, which from reading the code does not seem to ever clean its output. So if the order of JARs changes or if one is removed, extra JARs will remain and clog up the dexer.

I worked around this in my plugin by registering a doFirst action on all DexFileDependenciesTasks that deletes and recreates the task's output directory.

Wednesday, January 15, 2020

Directories may or may not have their own entries in ZIPs

Today I found that one of my Gradle plugins choked on a JAR file that should have been perfectly normal. My plugin tried to extract the JAR by creating a file on disk for each entry, which ended up including directories. That was surely a bug, but it was strange that only some JAR files triggered it. Specifically, JARs created by a Gradle Java project jar task caused the problem, but those created by an Android createFullJarVariant task did not.

Apparently it is optional for ZIP (JAR) "entries" to be created for directories, even if there are files in those subdirectories. I examined example JARs in PowerShell with the ZipArchive class, finding that only the crash-causing JAR had directories in its Entries collection.

Tuesday, January 14, 2020

Creating symbolic links on network shares requires the privilege on the remote host

Someone asked on Super User how to create symbolic links on network shares. Trying to create one with mklink, even after adjusting symlink evaluation settings with fsutil, failed with an access-denied error. This is because Windows requires a privilege to create a symbolic link, both on the computer issuing the command and the one hosting the network share. That is, the user one connects to the share host as must hold the "create symbolic links" privilege on that host. The privilege can be assigned in the Local Security Policy snap-in (secpol.msc).

Saturday, January 11, 2020

Atrophied shutdown.exe feature: the /unexpected switch

While disassembling shutdown.exe for reasons, I noticed special handling of the /i (interactive switch). That was expected based on the documentation's note that it must be the first switch, but after those checks there was also special handling of an /unexpected switch. Supplying it, at least on my machine, always results in this message:

Failed to open UI.

I can't find any documentation of an "unexpected" switch and it's not clear to me what it would do.

Thursday, January 9, 2020

EA Async Gradle plugin

A couple weeks ago I came across EA Async, which transforms Java methods containing "await" calls into a series of asynchronous, non-blocking tasks. This makes it easier to write nice asynchronous code for the JVM. EA Async can perform the transformation at runtime via a Java agent, at build time via a command-line utility, or at build time via a Maven plugin. Using a build system plugin integrates the transformation into the normal development workflow, which is very convenient. Of course, since there's only a Maven plugin, only users of the Maven build system got this benefit.

I like Gradle, another very widely used build system. So yesterday and today I went ahead with writing a Gradle plugin for EA Async. It was a little tricky to build a Gradle plugin with their Maven project, but once I got it compiling it was fairly straightforward thanks to my work on other Gradle plugins. There was a subtlety in taking advantage of the transformer's support for custom CompletionStage implementations. Their class files needed to be accessible through the classloader passed to the transformer, so I had to involve the compilation task's classpath and its output directory to make sure the CompletionStage implementations in the target project were recognized.

I made a pull request today which was merged. The plugin will probably be published in the next EA Async release.

Wednesday, January 8, 2020

Building Gradle plugins with Maven

Today I started writing a Gradle plugin module for a project that uses Maven as its build system. I know it's possible, with some fiddling, to involve a Gradle build in a Maven setup, but this project is managed by someone else and I wanted to stay with their preferred technology as much as possible.

Since Gradle plugins are ultimately just JARs, it's possible to compile them with Maven. Of course, this loses Gradle's convenient facilitation of writing Gradle plugins. Specifically, you need to add various dependencies yourself rather than just using gradleApi(). These <dependency>s generally look like:

<groupId>org.gradle</groupId>
<artifactId>gradle-something</artifactId>
<version>5.6.4</version>
<scope>provided</scope>

I'm currently using version 5.6.4 because it's the latest version of the Gradle 5 series, which supported by Android Studio and used by a lot of projects. The "provided" scope makes it used only for compilation, never bundled with the plugin. You may also need a reference to Groovy.

The various types are spread across quite a few artifacts. For a pretty straightforward plugin (doing some extra stuff with the results of Java compilation), I needed dependencies on gradle-core, gradle-core-api, gradle-base-services, gradle-model-core, gradle-language-jvm, gradle-language-java, gradle-platform-jvm, gradle-plugins, and gradle-tooling-api. To determine which artifact provides a class the compiler can't find, you can search the Gradle monorepository.

For adding the plugin metadata, the properties file can go in the same place under resources/META-INF that it would with a Gradle build.

Tuesday, January 7, 2020

Policy Plus - More list UI fixes

The list policy element might be the most finicky part of Policy Plus. Today I received an issue report from a user stating that adding entries to a list in a certain policy failed to make the policy Enabled. That specific policy had no affected Registry entries of its own, only a key populated by the list element. Since the list element was set to clear all existing values, it was always interpreted as evidence in favor of the Disabled policy state. I corrected this by also checking for list entries, which are evidence in favor of the Enabled state.

While investigating that specific policy, I found a problem in the Element Inspector. Even though the list element had a prefix attribute (albeit an empty one), the inspector said "no prefix." I corrected the conditional.

Monday, January 6, 2020

Windows RAID will be extremely slow when resynchronizing

A while ago I was called in to look at a server that was performing very slowly. Disk-bound operations were primarily affected. This server uses Windows RAID for redundancy and the mirror was in the process of resynchronizing. (It had recently lost power unexpectedly.) I suspect it was checking the entire volume for consistency, which uses a lot of I/O and slows down everything else trying to use the disk.

Sunday, January 5, 2020

Policy Plus - REG file tolerance

I got a report from a Policy Plus user that the Import REG feature could not handle comments. This is true; I wrote the REG parser for exactly the format emitted by the Registry Editor. Even whitespace differences could cause parse failures. So to make it easier to manually write REG files used by Policy Plus, I adjusted that parser. Comments and extra blank lines are now allowed.

I also happened to notice that searching for a certain policy made the search dialog say it found a hit but not actually display a result. Apparently I had only put the list-updating code in the progress-update, not completion, handler. So up to 19 policies could never be shown in the search results. I fixed that with a tweak to the search dialog.