Blog

PowerShell に git branch 情報を表示する

https://github.com/dahlbyk/posh-git

を利用すればいい。https://www.powershellgallery.com/packages/posh-git/1.0.0-beta4 PowerShell gallery からインストールすればいいです。

profile.ps1 に以下のように設定した。

Import-Module posh-git

function prompt {
    $prompt = & $GitPromptScriptBlock
    if ($prompt) { "$prompt " } else { " " }
}

$global:GitPromptSettings.DefaultPromptAbbreviateHomeDirectory = $true
$global:GitPromptSettings.EnableFileStatus = $false

jacoco で lombok で生成されたコードを無視したい

https://www.rainerhahnekamp.com/en/ignoring-lombok-code-in-jacoco/ https://projectlombok.org/features/configuration https://qiita.com/hrkt/items/aa8b8434d4e30a6b160f

lombok.addLombokGeneratedAnnotation = true を追加すればよろしい。Jacoco 側にそういう対応が入っている。

https://github.com/jacoco/jacoco/pull/513

/actuator/configprops で kotlin の @ConfigurationProperties つけてるビーンが出ないよってとき

configprops は constructor の引数を見ている。何も指定しないと java と同じく arg0 とかになって acutuator で設定が見れない。

↓gradle の場合は以下のようにオプションを指定しよう。

            compileKotlin {
                kotlinOptions {
                    jvmTarget = "1.8"
                    allWarningsAsErrors = true
                    javaParameters = true // ←これ!
                }
            }

NullPointerException の stacktrace が出ないケース

https://stackoverflow.com/questions/2411487/nullpointerexception-in-java-with-no-stacktrace https://stackoverflow.com/questions/4659151/recurring-exception-without-a-stack-trace-how-to-reset

hotspot だと最適化の都合で、出ないケースがあるらしい。

-XX:-OmitStackTraceInFastThrow を入れると全部出るようになるが、パフォーマンスに若干の影響があるのかな。試してない。

→ パフォーマンスはあんま問題なさそうだが、ディスク溢れないように気をつけよう、という識者の意見。

Skitch が catalina で使えなくなったので頑張って OSX のスクショ機能を使っていた

OSX のスクショ機能なんであんな使いにくいの。。モーダルにしなくてよくない?? って苦しんでたけど、

↓これで skitch をまた使えるようになった。安心。 https://neko11.com/macos-catalina%E3%81%AB%E3%81%97%E3%81%A6%E3%81%8B%E3%82%89skitch%E3%81%A7%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%8F%E3%81%AA/

compressed oops

https://blog.tiqwab.com/2019/07/21/openjdk-oop.html http://itdoc.hitachi.co.jp/manuals/link/cosmi_v0970/03Y1660D/EY160244.HTM https://www.baeldung.com/jvm-compressed-oops

cloc みたいな行数適当に数える奴

#!/usr/bin/env perl
use strict;

&main; exit;

sub main {
    my %white = map { $_ => 1 } qw/
        kt java pl js rb py
    /;

    my %result;

    for my $file (split /\n/, `git ls-files`) {
        my ($ext,) = ($file =~ m{\.([^./_-]{1,4}$)});
        $ext //= '-';
        my $lines = do {
            open my $fh, '<', $file;
            my $n = 0;
            $n++ while <$fh>;
            close $fh;
            $n;
        };
        $result{$ext} += $lines;
    }

    my $total = 0;
    for my $ext (sort { $result{$a} <=> $result{$b} } keys %result) {
        if (%white) {
            next unless $white{$ext};
        }

        my $r = $result{$ext};
        printf("%5s %10s\n", $ext, commify($r));
        $total += $r;
    }
    print "\n";
    printf("%5s %10s\n", "Total", commify($total));
}

sub commify {
    local $_ = shift;
    1 while s/^([-+]?\d+)(\d{3})/$1,$2/;
    return $_;
}

[kotlin] lateinit が初期化されてるか確認する

lateinit var foo: String である時に、lateinit 変数が初期化ずみか調べるには ::foo.isInitialized とすると良い

mysql 8.0.17 から 8.0.19 までは member は予約語

MEMBER; added in 8.0.17 (reserved); became nonreserved in 8.0.19

レポジトリの中に含まれる kotlin/java ファイル数を歴史込みで集計する

TSV が出るので、出たら tableau とかで集計する。

# XXX git checkout --force するんで手元がぶっ壊れるので注意してね! XXX
import subprocess
import os
from datetime import datetime, timedelta, date
 
 
print("ago\tlanguage\tfiles\tlines")
 
subprocess.run(['git', 'stash', '--quiet'])
subprocess.run(['git', 'checkout', '--quiet', 'master'])
 
for ago in range(1, 500, 10):
    cmd = '''git log --before '%d day ago' -n 1 --oneline "--format=format:%%H %%aI" | sed -e "s/T.*//" ''' % ago
    res = subprocess.run(['/bin/sh', '-c', cmd], capture_output=True)
    splitted = res.stdout.decode().rstrip().split(' ')
    commit = splitted[0]
    date = splitted[1]
    subprocess.run(['git', 'checkout', '--force', '--quiet', commit])
 
    kotlin_cnt = 0
    kotlin_lines = 0
    java_cnt = 0
    java_lines = 0
    for dirName, subdirList, fileList in os.walk('.'):
        for fname in fileList:
            if fname.endswith('.kt'):
                kotlin_cnt += 1
                kotlin_lines += sum(1 for line in open(f"{dirName}/{fname}"))
            elif fname.endswith('.java'):
                java_cnt += 1
                java_lines += sum(1 for line in open(f"{dirName}/{fname}"))
 
    print(f"{date}\tkotlin\t{kotlin_cnt}\t{kotlin_lines}")
    print(f"{date}\tjava\t{java_cnt}\t{java_lines}")

mutable なクラスがどのぐらい DI 対象になってるか調べたい

BeanPostProcessor で探せる。

import org.springframework.beans.factory.config.BeanPostProcessor
import org.springframework.context.annotation.Profile
import org.springframework.stereotype.Component
import java.lang.reflect.Modifier

@Component
@Profile("!real")
class MyProcessor : BeanPostProcessor {
    override fun postProcessAfterInitialization(bean: Any, beanName: String): Any? {
        val fields = bean.javaClass.declaredFields
        val packageName = bean.javaClass.packageName
        if ((!packageName.startsWith("org.springframework"))
            && fields.filter { !Modifier.isFinal(it.modifiers) }.count() > 0) {
            println("$bean has mutable field.")
        }
        return bean;
    }
}

[spring] @Configuration と @Component は違う、あるいは @Bean lite mode について

Spring の @Configuration は @Component の stereotype だと思っていたが、挙動が違うとのこと。

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html

@Bean Lite Mode というものがあって、@Component を利用すると CGLIB proxy を利用した aspect 系の処理が動かないらしい。

https://github.com/spring-projects/spring-framework/issues/14061

歴史を振り返ると、spring 3 の頃にバグ報告されたから lite mode ということにしたようにもみえる(深追いしてない)。

(全く知らなくてまつださんに教えてもらった)


【20191123 追記】

Spring framework 5.2 以後では、@Configuration(proxyBeanMethods=false) と書けるようになっているから、これを利用するのが良さそう(意図が明確なので)。

Spring framework 5.2 以後=Spring Boot 2.2 以後では WebClient のレスポンスの取り扱いが便利になっている件

https://twitter.com/making/status/1192216994147270656?s=20

WebClient からの response status などを取得しやすくなっている。 便利。

    val logger = LoggerFactory.getLogger(WebfluxSampleApplication::class.java)

    val url = "http://example.com/"

    val webClient = WebClient.builder()
            .build()
    val entityMono = webClient.get().uri("http://example.com/")
            .retrieve()
            .toEntity(String::class.java)

    logger.info("Sent http request. url=$url")

    val entity = entityMono.block()!!

    if (entity.statusCode.is2xxSuccessful) {
        val body = entity.body
        logger.info("HTTP request succeeded. url=$url status=${entity.statusCodeValue} length=${body?.length}")

        // process response data...
        println(body)
    } else {
        logger.info("HTTP request failed. url=$url status=${entity.statusCodeValue} body=${entity.body}")
    }

assertj 3.12.0 の breaking change がエグい

Iterator<Foo> fooIterable = getFooIterable();
assertThat(fooIterable)
    .extracting(Foo::getId)
    .isEqualTo(List.of(1,2))

みたいな書き方だったのが

Iterator<Foo> fooIterable = getFooIterable();
assertThat(fooIterable)
    .toIterable()
    .extracting(Foo::getId)
    .isEqualTo(List.of(1,2))

と書かなくてはいけなくなった。

該当のコミットはこれ。

https://github.com/joel-costigliola/assertj-core/commit/acafa142aa903afd611de812576e6e80e12e7964#diff-71630712302645dfc9a55762045740e6

もとのメソッドを Deprecated にして一旦置いといても良かったように思うのだが。。

`ClassLoader#getResources("")` の返り値が Java9 以後は jar:file: も含むようになっている

package com.example;

public class Example {
    public static void main(String[] args) throws java.io.IOException {
        System.err.println("JVM: " + java.lang.management.ManagementFactory.getRuntimeMXBean().getVmVersion());

        ClassLoader classLoader = Example.class.getClassLoader();
        System.out.println(classLoader);

        java.util.Enumeration<java.net.URL> resources = classLoader.getResources("");
        while (resources.hasMoreElements()) {
            System.out.println("-- " + resources.nextElement());
        }
    }
}

このようなプログラムの実行結果が、Java 9 以後では異なる。

Run with Java 8

JVM: 25.201-b09
sun.misc.Launcher$AppClassLoader@2a139a55
-- file:/Users/tokuhirom/work/urlclassloader-behavoiour/build/classes/java/main/

https://docs.oracle.com/javase/8/docs/api/java/lang/ClassLoader.html#getResources-java.lang.String-

Run with Java 11

JVM: 11.0.1+13-LTS
jdk.internal.loader.ClassLoaders$AppClassLoader@799f7e29
-- file:/Users/tokuhirom/work/urlclassloader-behavoiour/build/classes/java/main/
-- jar:file:/Users/tokuhirom/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy-agent/1.10.2/fbfe9bf099287c35b8336ea9da194f301a112a11/byte-buddy-agent-1.10.2.jar!/META-INF/versions/9/

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ClassLoader.html#getResources(java.lang.String)

こんな感じ。

【解決済み】 Boot 2.2 で AbstractRoutingDataSource しかない場合にアプリケーションが起動しない問題について

https://github.com/spring-projects/spring-boot/issues/18661

Boot 2.2 で scan 対象に AbstractRoutingDataSource しかない場合に、アプリケーションが起動しないという問題を見つけたので報告したところ、シュッと対応してくれた。 次回のリリースで治る見込み。

https://github.com/spring-projects/spring-boot/commit/c5138c56ff4c4fa7ec6c4ff2823e2b86bc7e0ef1

待てない人は、management.health.db.enabled=false を設定すれば良いとのこと。

ただ、本来は AbstractRoutingDataSource の先の DataSource がある場合はそれを DI 対象にしておくのが良いです。

Jackson 2.10 についてのメモ

ざっくりいうと


Jackson 2.10 features - @cowtowncoder - Medium を参考のこと。

Jackson 2.10 は、Jackson 3 に向けての Migration 用のリリースになっている。2.9までのインターフェースも @Deprecated 状態で残しつつ、3以後のあたらしいインターフェースも実装されている。今のうちに新しいインターフェースに移行しておくと後々楽になる、と思う。 (Spring Boot 2.2 は Jackson 2.10 に依存している)

2.10 では以下の3つのメジャーなイシューを解決している。

  1. デシリアライズ時におけるセキュリティイシューの根本解決
  2. 3.0 における新しいインターフェースへの移行促進
  3. module-info.class 関連

で、1 はまあいいとして、2 が重要だなと思っている。新しいインターフェースがどういうものかというと以下のような感じ。

の2点が新しいインターフェースの特徴。mutable な deserializer とか、最近は流行らないからね。

Jackson 3.0 以後では ObjectMapper は configuration 系のメソッドは持たない。

Spring Framework 5.2 からは WebClient の retrieve でちゃんと http status を取れる

Spring Framework 5.2 がリリースされた。

Support for Kotlin Coroutines.

も大きいのだが、、個人的には以下に注目したい。

Refinements to WebClient API to make the retrieve() method useful for most common cases, specifically adding the ability to retrieve status and headers and addition to the body. The exchange() method is only for genuinely advanced cases, and when using it, applications can now rely on ClientResponse#createException to simplify selective handling of exceptions.

これまで、Webclient を利用した場合、.retreive().bodyToMono(String.class) などとして response body のみを取得するメソッドしかなく、異常に使いづらかった。 HTTP Status Code が 2xx 以外の場合には例外が上がる設計になっているのはいいのだが、現実的にはどの HTTP Status Code かは例外ではなく通常の処理としてハンドリングしたいというケースも多いのである。

Spring Framework 5.2 以後では以下のように記述可能になった。

        WebClient client = WebClient.create();
        Mono<ResponseEntity<String>> responseEntityMono = client.get()
                                                                .uri(url)
                                                                .retrieve()
                                                                .toEntity(String.class);

        ResponseEntity<String> responseEntity = responseEntityMono.block();
        assert responseEntity != null;
        log.info("url={} status={} headers={} body={}",
                 url,
                 responseEntity.getStatusCodeValue(),
                 responseEntity.getHeaders(),
                 responseEntity.getBody());

便利。

Python 2 で GMT で出てるやつを適当に JST にする

from datetime import datetime, timedelta

def to_jst(src):
    return str(datetime.strptime(src, '%d %b %Y %H:%M:%S %Z') + timedelta(hours=9)) + " JST"

print(to_jst('26 Sep 2019 03:56:25 GMT'))

依存とかなしでやる。

sparksql で emoji/pictogram を含む行を検出する

select text from table where dt='20190911' and text rlike '[\\uD800-\\uDFFF]'

とかでとりあえず良さそう。