mike-neckのブログ

Java or Groovy or Swift or Golang

昨日書いたダメなコード

ヒドイコードを書いたので忘れないようにメモっておく(忘れないとは言ってない)

前提

  • Java7
  • guava18.0 使用

やりたいこと

Optional の中の値に対して filter して isPresent の結果がほしい

書いたコード

例: Optional<Integer> に対して value > 0filter をかける

final Optional<Integer> object = ...;
final boolean result = object.transform(new Function<Integer, Optional<Integer>>() {
  @Override
  public Optional<Integer> apply(final Integer value) {
    if (value > 0) {
      return Optional.of(value);
    } else {
      return Optional.absent();
    }
  }
}).or(new Supplier<Optional<Integer>>() {
  @Override
  public Optional<Integer> get() {
    return Optional.absent();
  }
}).isPresent();

どう書けば読めるコードになったか

final Optional<Integer> object = ...;
final boolean result = object.isPresent() && object.get() > 0;

Java8なら

final Optional<Integer> object = ...;
final boolean result = object.filter(v -> v > 0).isPresent();

Java8での書き方に慣れすぎているがゆえに、 filter/flatMap のない guava の Optional の制限プレーはなかなか厳しい…あと問題は簡単のために value > 0 みたいになっているけど、実際は Predicate<T> が与えられたメソッドでのとりまわしなので、どう書けば…のところのようにキレイに書けたかどうかは自信ない

Java9でGradleを動かす

単なるメモ

Javaのバージョンが2017/10/22現在で9.0.1で、Gradleのバージョンが4.2.1以下だとJavaのバージョンをパースできないためにGradleを実行できない

Gradle4.2 での実行結果
$ java --version
Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8
java 9.0.1
Java(TM) SE Runtime Environment (build 9.0.1+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.1+11, mixed mode)

$ ./gradlew --version
Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8

FAILURE: Build failed with an exception.

* What went wrong:
Could not determine java version from '9.0.1'.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

* Get more help at https://help.gradle.org
Gradle4.2.1 での実行結果
$ java --version
Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8
java 9.0.1
Java(TM) SE Runtime Environment (build 9.0.1+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.1+11, mixed mode)

$ ./gradlew --version
Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/Users/mike/.gradle/wrapper/dists/gradle-4.2.1-bin/dajvke9o8kmaxbu0kc5gcgeju/gradle-4.2.1/lib/groovy-all-2.4.12.jar) to method java.lang.Object.finalize()
WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

------------------------------------------------------------
Gradle 4.2.1
------------------------------------------------------------

Build time:   2017-10-02 15:36:21 UTC
Revision:     a88ebd6be7840c2e59ae4782eb0f27fbe3405ddf

Groovy:       2.4.12
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM:          9.0.1 (Oracle Corporation 9.0.1+11)
OS:           Mac OS X 10.12.6 x86_64

Gradleでプラグインを利用する方法

今更なタイトルですが…

incubatingなAPIで plugins DSLと呼ばれている方法です。plugins DSLはgradleのplugin repositoryにあるプラグインをid(とバージョン)だけで引っ張ってこれるようにする方法で、gradleの2.10くらいに登場しています。gradleにデフォルトに添付されているプラグインとgradle のplugin repository にあるプラグイン以外をこのDSLで指定する方法は登場した当初はなかったのですが、最近はsettings.gradleで頑張ればできるということ(そして、multipleなプロジェクトになるとすごく便利になるということ)を教えてもらったので、忘れてもいいようにメモしておきます。


実例としてJUnit5のプラグイン org.junit.platform.gradle.plugin を使います。

plugins ブロックの中に適用するプラグインのidを記入します。
plugins {
  id 'java'
  id 'org.junit.platform.gradle.plugin'
}

gradleの推奨するプラグインの名前はパッケージ名と同じような名前である id.namespace + id.name のようです。JUnit5のプラグインの場合ですと、 org.junit.platform.gradleid.namespacepluginid.name です。雑に言えば、最後のドットから右が id.name 左が id.namespace です。

org.junit.platform.gradle.plugin は gradle plugin repository にはないので、プラグインに使われるartifactの解決をしなければなりません。artifactを解決する方法として pluginManagement DSLがありそれを settings.gradle ファイルに記述します。
pluginManagement {

}

pluginManagement DSL内部では二つのブロックを書けます。一つは repositories ブロックであり、こちらは名前からわかるようにレポジトリーのURLを記述していきます。もう一つは resolutionStrategy であり、こちらにはプラグインの名前からどのartifactを使うかを記述します。

repositories ブロックには1つのブロックと1つのメソッドが提供されています。 maven ブロックではgradle plugin repository以外のレポジトリーのURLを記述します。 gradlePluginPotal メソッドを呼び出すと gradle plugin repository を使う宣言ができます。gradle plugin repository が不要な場合は gradlePluginPotal を呼び出さなければよいです。なお、maven centralやjcenterなどのおなじみのメソッドが生えていないので、それぞれのURLを書かなければなりません。JUnit5の例では、プラグインのjarはmaven centralにあるので、maven ブロックの中でmaven central のURLを指定します。
repositories {
  maven {
    url ('http://repo1.maven.org/maven2/')
  }
  gradlePluginPortal()
}
resolutionStrategy ブロックの中に eachPlugin ブロックを書いて、その中でプラグインのid(およびバージョン)とその扱いを決定します

requested というプロパティに指定したプラグインのidなどの識別する情報が含まれています。 requested の中には3つのプロパティ(idmoduleversion)が含まれていますが、多くの場合は id 以外は null です。 requested.id の中には次のプロパティが含まれています。

  • id: String - id の文字列表現
  • name: String - idname(最後のドットから右)
  • namespace: String - idnamespace(最後のドットから左)

これを if 文などでマッチングさせて、 eachPlugin ブロックに生えている useModule メソッドから artifact を指定します。artifact の指定方法は dependencies ブロックでの指定方法と同じです。JUnit5の例ではプラグインのidが org.junit.platform.gradle.plugin の場合に org.junit.platform:junit-platform-gradle-plugin:1.0.0 を使いたいので、そのようなマッチングと useModule の指定をします

resolutionStrategy {
  eachPlugin {
    if (requested.id.id == 'org.junit.platform.gradle.plugin') {
      useModule 'org.junit.platform:junit-platform-gradle-plugin:1.0.0'
    }
  }
}

以上の内容を合わせると次のようなスクリプトができます

pluginManagement {
  repositories {
    maven {
      url 'http://repo1.maven.org/maven2/'
    }
  }
  resolutionStrategy {
    eachPlugin {
      if (requested.id.id == 'org.junit.platform.gradle.plugin') {
        useModule 'org.junit.platform:junit-platform-gradle-plugin:1.0.0'
      }
    }
  }
}

なお、 pluginManagement ブロックは settings.gradle の中で一番最初に書かないといけないようです


ここまで試した感じですが、わりと面倒でしたが、 subprojects ブロックで新しいプラグインを読み込ませる方法などを考えると、致し方ないのかなぁという気持ちになります

あと、途中文字が太くなってたりしますが、個々のタイトルを考えるのが面倒で、すべてh5で記述していました…

git secretsとJavaのserialVersionUIDの相性が悪い

念のためにgit secretsを入れて、aws providerを設定していますが、ちょくちょくJavaのプロジェクトのコミットで失敗が発生してた。

コミットのエラーログを調べると、serialVersionUID private static final long serialVersionUID = -8419924062942848690L;aws providerのデフォルトの禁止パターン [A-Z0-9]{20} と一致していることが原因のよう。

ドキュメントをざっと読みましたが、特定のファイルだけ除外するとか、パターンに当てはまったものをさらにパターンにかけるみたいな方法がないので、プロジェクトのルートに .gitallowed ファイルを用意して、

[0-9]{19}L

というパターンを追加した。

これで、とりあえずコミットできるようになった。