u-ryo's blog

various information for coding...

Comparable of Java

| Comments

小数と分数を表すclassなど似たような役割なら、 classを超えてComparableでもいっか、 とか思ってしまって反省しています。

Effective Javaにあるんですね、 【Effective Java】項目12:Comparable の実装を検討するって。 compareToを定義するならequalshashCodeも定義すべきだし、 a.compareTo(b)==0a.equals(b)==trueが違うなら、 compareToで比較するTreeSetequalsで比較するHashSetで 結果が違っちゃうんですね。 compareToが違うclassの比較を許すなら、 equalsでも許さねばならず、 流石にそれはおかしいだろう、 ということでしょうか。 実際、

クラスが適切にパラメータ化されていれば、ClassCastExceptionがスローされます。 契約は、クラス間の比較を排除してはいませんが、 リリース1.6では、Javaプラットフォームライブラリーのどのクラスも、 クラス間の比較をサポートしません。

【Effective Java】項目12 Comparableの実装を検討する

Daemonizing Jhipster Application

| Comments

JHipsterのapplicationで、 OS(Ubuntu 18.04)起動時にapplicationもdaemonとして自動起動するようにするには。

きっとSpring Bootでdaemonizeする方法を探ればいいと思って、 61. Installing Spring Boot Applicationsにあるように、

1
2
3
bootJar {
	launchScript()
}

でもこれ、Spring Boot 2での話で、 こちとらまだJHipster 4.14.4、Spring Bootは1.5です。 そんなものはない、と当然失敗します。 なのでもうちょっと古い記事を探しました。

spring bootアプリの起動スクリプトを作るを見て、

1
2
3
4
5
apply plugin: 'spring-boot'

springBoot {
    executable = true
}

としたんですが、plugin 'spring-boot'はないと言われ、 executable = truebuild.gradleに既に書いてありました。

そもそもそんなことしなくても、 Using in Production にあるように、

1
$ gradle bootRepackage -Pprod

でexecutable war作れるんですね。 で、それを/etc/init.d/にsymlinkすればいいだけという。 -Pprodを付けないとdevelopment versionになってしまいます。 あとは、update-rc.d appname defaultsで登録すれば良いです。

Time/Date Display Format on GNOME Shell

| Comments

Ubuntu 18.04使ってるんですが、 Unityから変わったGNOME Shellの真ん中に出てるのが 曜日と時分だけなので、不便でした。 日付を確認したくなることもあるし、 秒がないと動いているのか止まっているのかよくわからなかったりとかするんですよね。 どうするのかなー、と思って調べたら、一発でした。 GNOME Shellのパネルに日付を表示する方法

1
2
$ gsettings set org.gnome.desktop.interface clock-show-date true
$ gsettings set org.gnome.desktop.interface clock-show-seconds true

tabによる補完で調べると、他にはclock-show-weekdayがあり、 それは既にtrueになってるんでしょうね。

XML Marshaling in Spring Boot

| Comments

Spring Boot applicationで単一xml fileを返すREST作ってあって、 そこではxmlと同じ構造のJava bean作って返すだけで、 marshalingについてはframework側がよしなにやってくれました。 client側がHTTP Request HeaderにAccept: application/xmlとすれば。 そうでないとjsonになります。まぁそれはそれでいいんです。いいと思いました。

その後、複数xmlをまとめてZIPにして返すRESTを求められました。 そうすると、自分でXMLにmarshalingしなければなりません。 と、Gson? でもbuild.gradle見ると折角jackson読み込んでいるようなので、 jacksonでmarshalしました。new ObjectMapper()して、 mapper.write(ZipOutputStream)みたいなことすると、 一回(=one file)書いただけでstreamを勝手に? closeするようなので、 一旦Stringにしてからzos.write()しました。 また、build.gradlecompile "...jackson-dataformat-xml"も必要でした。

しかしそうすると、今度はXMLを返すRESTの方で、 返されるXMLの形が微妙に違っていました。具体的には、

  1. @XmlRootElement(name=...)で指定した名前が効かない
  2. XML Object中でList要素がnestされる (<object></object><object></object>...だったのが <object><object></object><object></object>...</object>に)

@XmlRootElementはJAXBのannotation (javax.xml.bind.annotation.XmlRootElement)で、 これが効かないというのだからJAXBが効いてないのだろうと思い、 そういえばbuild.gradlejackson-dataformat-xmlって書いたな、 というのを思い出し、 ZIP中でのXMLのmarshallingをJAXBのものでやるようにして build.gradleからjackson-dataformat-xmlを追い出したら、 元に戻りました。

JAXBでのmarshalling、ちょこっと面倒ですが、 context = JAXBContext.newInstance(Bean.class)して marshaller = context.createMarshaller()作って、 marshaller.marshal(bean, zipOutputStream)すればいいんですね。

New Map on Typescript

| Comments

Angular5で、といいますかTypescriptで、 Mapを一度にnewしたかったんですが、 どうするのかなーって。

new Map()して.set()でmethod chainで繋いで行くのもありますが、 一番簡単なのは、 new Map([['key1','val1'],['key2','val2'],['key3','val3']])という ようなnested arrayでしょうか。 cf. Map & Set

ただ、こうするとmap['key1']delete(map['key2'])は効かず、 map.get('key1')map.delete('key2')としないとならない、 というのにハマりました。