IT社畜犬くわっちょのはてな

青森の片隅で働く、フルスタックエンジニアに憧れる器用貧乏なIT社畜犬の遠吠え

Thymeleaf Layout Dialectを2.5から3.0に上げたらプロセッサが効かなくなった日

Spring Boot: 2.5系 Thymeleaf: 3.0系 を使っている場合に Thymeleaf Layout Dialect を2.5系から3.0.0に上げた場合に layout:decorate="~{layout}" といったプロセッサが効かなくなってしまう事態に遭遇。

画面表示時のThymeleafのログを見ると、Thymeleaf Layout Dialectが参照されなくなっている。

2.5系では以下のようなものをを書かなくても動いたが、 3.0.0ではちゃんと書かなきゃ動かない模様。

@Configuration
public class ThymeleafConfig  {

    @Bean
    public LayoutDialect layoutDialect() {
        return new LayoutDialect();
    }

}

今まで書かなくても動いていたのがよくなかったのか、 それともこれは意図しない動作なのか、 いまいちわからなかったがまあ、ちゃんと書くべきだろう。

青森で約一年働いて感じた事などなど

はじめに

この記事はIT地方エンジニア Advent Calendar 2018 23日目の記事です。

https://adventar.org/calendars/3086

2017年9月に東京から青森にUターンし、早1年が経過。 去年の年末に書いた記事が以下。 itdogkuwaccho.hatenadiary.com

その当時とは状況もいろいろ変わってきてますので、改めてまとめてみました。

各種ツールの導入はやはり遅れがち

自分達だけが使えても他の人、会社が使えるとは限らない。 Git一つとっても、外注先に出来る人がいないなんてしょっちゅう。 また、アナログなやり方を好む傾向もあるのでツールの導入は余程のトップダウン形式でないと進まない。

人が足りない。募集しようにも来ないか雇う余裕がない

賃金格差や客がいないとかあれど、本当に人を集めて維持し続けるだけでも一苦労。

特定の技術だけでは生き残れない

人がいないため、どうしても一人で見るべき部分が多くなってしまう。 そのため一人である程度は何でもできないといけない。 核となる一つの技術だけでなく、周辺の技術も必要。 ある程度無茶ぶりにも絶えれる力が必要。

地方でITが活躍するのは無理なのか?

無理ではないが人に依存しすぎる体制をもっと変えていけないと厳しい。 また、都会以上に「ITがそれだけでは価値を産まない」 サービスありきでITが価値を生むので、人月商売のようなものは生き残れない。 だからこそエンジニア自身の実力がサービスの質に非常に影響するので、都会以上に勉強が必要。

とはいえ

人が少ないのも事実......。 お客様がどうしても東京中心なので、リモートワークや企業の地方移転などが進まないと根本的には変わらない。 実際、毎週のように東京出張を繰り返す人もいるので東京一極集中が変わらないと一時的な対策にしかならないと感じる。

楽しいか?

一人でなんでもやってみたいという人なら楽しめる。 ただしその分やることは多い。

残業は?

22時になると大半の会社は閉まるので、東京のように23時24時が当たり前という事はない。 けれど、やはりやることが増えると残業が増える。 通勤時間が(場所にもよるが)短くなる分まだいいかも。

最後に

地方は確かに人が足りない。けれど、人を増やせばどうにかなるわけでもない。 東京で地方の会社の仕事をやって、希望する人が行き来できる仕組みを作るように、 柔軟に働き場所を変えられる仕組みを作っていかないと、いくら給料をよくしても来ない。

各地でリモートワークや技術向上などで色々と動きはあるようなので、 これからは地方同士のつながり、東京との連携をもっと強めていかないといけないと感じています。 まずは地方同士のつながりをどう作っていくかが今後の課題だと思っています。

Tomcatだけで特定の画面だけBASIC認証を外したい時

正直、普通にHTTPサーバー側で制御しろという話なのだが......

web.xmlにこんな感じに security-constraint を2つ用意した上で必要なものをBASIC認証のレルムから外す事でできる。

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>no auth</web-resource-name>
            <url-pattern>/sample.html</url-pattern>
            <url-pattern>/hoge/*</url-pattern>
        </web-resource-collection>
    </security-constraint>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>User Basic Auth</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>samplerole</role-name>
        </auth-constraint>
    </security-constraint>

    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>User Basic Auth</realm-name>
    </login-config>

    <security-role>
        <role-name>samplerole</role-name>
    </security-role>

特定の画面だけ認証をかけるだけでなく、逆も一応できる。

東京の会社を退職し青森にUターンした話

itdogkuwaccho.hatenadiary.com

渋谷のIT企業を退職して早いものでもう三ヵ月ほど過ぎました。 転職の理由などは上記にある通りなので青森に転職しての感想など。

10年近く前のコードが現役

これは青森に限った話ではないでしょうが、10年前以上のJavaソースが脈々と使われているのを見るとなんともなあという気持ちになります。

開発環境は最新。本番環境は長期稼働

開発用PCはWindows10など最新のものなのですが、本番機は結構キャリア長め。もちろんアップデートはされているのですが、社内用テスト環境がWindowsXPWindows2000とかはある

外注探すのも一苦労

プログラミングを外注しようにもSI企業はほとんどないので仕事がなくてもエンジニアをキープするためにお金を払う......とかがある。

求められるスキルは幅広いものになる

エンジニアを集めようにも東京のようにはいかないので、どうしても全般的に詳しい人が求められる。

仕事の自由度は高い

その分自分で決められる範囲は広い。責任は伴うけれど。

残業はほとんどない

定時で帰る人が結構多いです。

最後に

青森に帰ってきたこと自体は後悔していません。 しかし、時々東京での騒がしい日々は懐かしくなります。 今の自分を大切にしながら、青森で、今のところで、役に立てるように頑張っていきます。

JDK 1.8.0_152での無制限強度暗号ポリシーの変更方法 ※Java9も少し含む

今更だけどJCE policyの解除方法がかわったっぽいので検証。

検証プログラム https://github.com/kuwaccho/Verification/blob/master/src/AesCryptTest.java

  • Java8の場合

JDKJDK 8u152を使用

keyが128bitの場合

PLAIN TEXT: (・ω・`U)
ENCRYPTED : cOipgSYWi6/AXsfjTNmysQ==
DECRYPTED : (・ω・`U)

何もしないでkeyが256bitの場合

PLAIN TEXT: (・ω・`U)
java.security.InvalidKeyException: Illegal key size
    at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1039)
    at javax.crypto.Cipher.implInit(Cipher.java:805)
    at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
    at javax.crypto.Cipher.init(Cipher.java:1396)
    at javax.crypto.Cipher.init(Cipher.java:1327)
    at AesCryptTest.main(AesCryptTest.java:23)

jdk1.8.0_152/jre/lib/security/java.security の826行目にある以下のプロパティのコメントアウトを解除

#crypto.policy=unlimited
↓
crypto.policy=unlimited

結果

PLAIN TEXT: (・ω・`U)
ENCRYPTED : 3858uw2xCZPbmpV9xs4/eA==
DECRYPTED : (・ω・`U)

ちなみに

jdk1.8.0_152/jre/lib/security/policy
                ├─limited
                │      local_policy.jar
                │      US_export_policy.jar
                │
                └─unlimited
                        local_policy.jar
                        US_export_policy.jar

とあるのでこのあたりを切り分けているんだろうなあ。

あと Java™ SE Development Kit 8, Update 152 Release Notes にこんなこと書かれているので注意。

Because the old JCE jurisdiction files are left in <java-home>/lib/security, they may not meet the latest security JAR signing standards, which were refreshed in 6u131, 7u121, 8u111, and later updates. An exception similar to the following might be seen if the old files are used:

Caused by: java.lang.SecurityException: Jurisdiction policy files are not signed by trusted signers! at javax.crypto.JceSecurity.loadPolicies(JceSecurity.java:593) at javax.crypto.JceSecurity.setupJurisdictionPolicies(JceSecurity.java:524)

  • Java9,10の場合

Java9,10の場合はjava.securityの場所がconf以下に変更になっているのと、最初からコメントアウトはされていない(832行目付近参照)ので 何もしないで動く。

JDK 9 Readme

The default JCE policy files bundled in this Java Runtime Environment allow for "unlimited" cryptographic strengths.
For convenience, this software also contains the historic "limited" strength policy files which restricts cryptographic strengths. To use the limited strength policy, instead of the default unlimited policy, you must update the "crypto.policy" Security property (in /conf/security/java.security) to point to the appropriate directory. You are advised to consult your export/import control counsel or attorney to determine the exact requirements of your location, and what policy settings should be used.

弁護士とちゃんと相談しろよという一文がなんかひっかかるが。

AESの暗号鍵列長のポリシーが変更になった理由は何なのかは気になる。 アメリカ政府の方針変換なのか、そもそも年数がたちすぎているからかセキュリティの問題か......。

メジャーなRDBMSでREPLACE関数が空文字を返すかnullを返すか調べてみた

青森に帰ってきてから仕事ではOracleを使うようになった。 今までは予算の都合から使える事はなかったのだが......。

そんな中、2017年にもなってOracleを使う人ならまず引っかからないであろう奇妙な挙動に引っかかる。

SELECT 'TEST'
FROM dual 
WHERE
  REPLACE (REPLACE (' テスト ', ' ', ''), ' ', '') = 'テスト' 
  AND REPLACE ('//', '//', '') = ''

上記SQL、実際に実行していただければわかると思うが 結果は何も出ないのだ。

原因は5行目、ここを空文字比較しているのが問題。 Oralceでは上記のようにREPLACEを行って空文字になるとnullと解釈する。

なので上記のSQL

SELECT 'TEST'
FROM dual 
WHERE
  REPLACE (REPLACE (' テスト ', ' ', ''), ' ', '') = 'テスト' 
  AND REPLACE ('//', '//', '') IS NULL

と IS NULL を使えばちゃんとtrueになって値が取得できるというわけだ。

......いやまて、なぜ0byte文字列とnullが同値扱いになる? ※実際には空文字比較でfalseなので空文字はnullへの不可逆変換なわけだが。

これに関しては

と教えていただいてそこにはさらに

By the time that the SQL standard came around and agreed that NULL and the empty string were distinct entities, there were already Oracle users that had code that assumed the two were equivalent. So Oracle was basically left with the options of breaking existing code, violating the SQL standard, or introducing some sort of initialization parameter that would change the functionality of potentially large number of queries. Violating the SQL standard (IMHO) was the least disruptive of these three options.

とあるので標準化が進む中で、既にもう既存コードが置き換えするのが難しいくらいになってきているのであえて規則に違反する道を選んだのではないかと言われている。 確かにこれはうなずける部分もある。上記のSQLの挙動が変われば影響あるシステムはいくつもあるはずだ。

しかしそれはあくまでnullとempty Stringの問題であってREPLACE関数まで同様にするのは正直どうかと思う。 REPLACEで置き換えてempty Stringになった時点でもうnullと比較しないといけないということじゃないか。

SELECT 'TEST'
FROM dual 
WHERE
  REPLACE (REPLACE (' テスト ', ' ', ''), ' ', '') = 'テスト' 
  AND REPLACE ('//', '//', ' ') = ' '

あるいはまあこう書くのも一つの手なのかもしれないが、なんだかこれもバッドノウハウだと思う。 空白で置き換えるというのがややこしい。

ついでなのでメジャーなRDBMSでREPLACE関数で空文字になった場合にどのような挙動をするか調べてみた。

一番最後のは趣味だが......。 結果は案の定だったけれど一応検証用クエリを載せる。 テーブルを作成する必要がないからDBをインストールした状態であればすぐに追従はできるはずだ。

SQL Server

SELECT NULLIF(REPLACE ('//', '//', ''), '') ⇒ null
SELECT NULLIF(REPLACE ('//', '//', ''), null) ⇒ 空文字

※REPLACE結果は空文字扱い

Oracle

上記のとおり空文字はnull扱い これを流せば「(・ω・`U)null」となるはずだ。

SELECT
  '//'
  , NVL2(REPLACE ('//', '//', ''), '(・ω・U)null以外', '(・ω・`U)null') 
FROM dual 
WHERE
  REPLACE (REPLACE (' テスト ', ' ', ''), ' ', '') = 'テスト' 
  AND REPLACE ('//', '//', '') is null

MySQL

mysql> SELECT ISNULL(REPLACE ('//', '//', '')) as a;
+---+
| a |
+---+
| 0 |
+---+

※REPLACE結果は空文字扱い

PostgreSQL

SELECT NULLIF(REPLACE ('//', '//', ''), '') ⇒ null

※REPLACE結果は空文字扱い

DB2

SELECT COALESCE(REPLACE ('//', '//', ''),'0') FROM SYSIBM.DUAL ⇒ 空文字

※REPLACE結果は空文字扱い

SQLite3

SELECT ifnull(REPLACE ('//', '//', ''),'0')  ⇒ 空文字

※REPLACE結果は空文字扱い

SQL Anywhere

SELECT ifnull(REPLACE ('//', '//', ''),'0', '1') ⇒ 1

※REPLACE結果は空文字扱い

まとめるまでもないがまとめると

RDBMS null == empty String
SQL Server false
Oracle true
MySQL false
PostgreSQL false
DB2 false
SQLite3 false
SQL Anywhere false

とまあ、Oracleだけがぽっかりと奇妙な挙動をしてくれている。 他のDBも概ねこうなのだろうな......。

ちなみに

https://docs.oracle.com/cd/E16338_01/server.112/b56299/sql_elements005.htm

Oracle Databaseは現在、長さが0(ゼロ)の文字値をNULLとして処理します。ただし、この処理はOracleの今後のバージョンでも継続されるとはかぎらないため、空の文字列をNULLとして処理しないことをお薦めします。

とあるから上記非推奨なんじゃないかとは思う。 だったら空文字でもtrueにするようにすればよかったと思うのだが......

2017年になっても上記の挙動は変わってないのがよくわかる事案だった。

退職のお知らせ

2013年より約4年間勤めておりました会社を2017年8月31日をもって退職することとなりました。 同時に渋谷を離れ故郷に戻り、同じようにITの仕事に携わることに相成りました。

在職中は主にビックデータの解析ツールの開発や各種フレームワークを用いた開発、保守運用など様々な業務に携わることができました。 また、mongoDBやRedisといったNoSQLをがっつり触れたこと、 MySQLPostgreSQLのバックアップやリストアといった普段なかなかやれることがない事象に対処することができたこと、 進捗管理についても線引きからじっくり携わる事ができたこと、 など、非常に面白いことに携わる事ができたのは非常に幸運であったと思っています。

次は東京を離れ故郷で地方のITに携わる事になります。 同じIT業界とはいえ、東京と地方ではまた変わってくると思うので、その違いに早くなじめるように頑張ろうと思います。

最後になりますが、東京でお会いした皆さまには本当にお世話になりました。 少し離れてしまいますが、今後ともTwitter含めちょくちょく発信はし続けていくつもりですので、どうぞよろしくお願いいたします。