Lazy Diary @ Hatena Blog

PowerShell / Java / miscellaneous things about software development, Tips & Gochas. CC BY-SA 4.0/Apache License 2.0

日本のSIer的なソフトウェア工学が必要とされる前提条件

以下のような前提条件が満たされると、日本のSIer的なソフトウェア工学の有用性が高くなるのかな?という想像。

  • 解雇規制が強い(↔カリフォルニア州の雇用法。突然の解雇が合法 *1
  • 雇用の流動性が低い(↔エストニア等で高い。アメリカは中位で、日本は非常に低い *2
  • 派遣法や類似の法律で、派遣労働者の能力に対する責任を派遣元事業主が負う(ここは調べたけど分からなかった)
  • ソフトウェアが無形固定資産になる(これは米国基準でも同じみたい *3
  • ソフトウェアの内製が多い(↔ソフトウェア開発者の所属企業の偏りによる推測。*4
  • 企業会計に継続性原則がある(↔IFRS *5
  • Fixed Firm Price(請負契約)による契約が多く、それ以外の契約方法のハードルが高い
  • 受入検査を行う調達部門の独立性が高く、発注内容の変更が認められにくい

What is the difference of RFC 5322, RFC 821 and implementations of javax.mail.internet.InternetAddress ?

# Address Oracle JavaMail 1.6.2 GNU JavaMail 1.1.2 Geronimo JavaMail 1.8.4 RFC 5322 RFC 821
1 abc.def@example.com valid valid valid valid valid
2 "abc.def"@example.com valid invalid valid valid valid
3 "abc.\n def"@example.com valid invalid valid invalid invalid
4 "abc"."def"@example.com invalid invalid valid valid valid
5 'abc.def'@example.com valid valid valid valid valid
6 'abc.\n def'@example.com invalid invalid valid invalid invalid
7 'foo'.'bar'@example.com valid valid valid valid valid
8 (abc)abc.def@example.com valid invalid valid valid valid
9 (abc) abc.def@example.com valid invalid valid valid valid
10 <abc.def@example.com> valid invalid valid invalid invalid
11 <abc\n .def@example.com> invalid invalid valid invalid invalid
12 <abc"def"ghi@example.com> invalid invalid invalid invalid invalid
13 foo bar <abc.def@example.com> valid invalid valid invalid valid
14 'foo bar' <abc.def@example.com> valid invalid valid invalid valid
15 "foo bar" <abc.def@example.com> valid invalid valid invalid valid
16 abc.def@[example.com] valid invalid valid valid valid
17 abc.def@[exa mple.com] invalid invalid valid valid valid
18 abc.def@[exa\nmple.com] invalid invalid valid invalid invalid
19 abc.def@[example."hoge".com] valid invalid valid valid valid
20 ,abc.def@example.com valid invalid invalid invalid invalid
21 ;abc.def@example.com valid invalid invalid invalid invalid
22 abc..def@example.com invalid valid invalid invalid invalid
23 abc.def.@example.com invalid valid invalid invalid invalid
24 .abc.def@example.com invalid valid invalid invalid invalid
25 ⛄bc.def@example.com valid valid invalid invalid invalid
26 abc.def@localhost valid valid valid valid valid
27 abc.def@e.c valid valid valid valid valid
28 abc.def@e.co valid valid valid valid valid
29 abc.def@-.com valid valid valid valid valid
30 abc.def@203.0.113.1 valid valid valid valid valid
31 ABC.DEF@example.com valid valid valid valid valid
32 "<script>" <abc.def@example.com> valid invalid valid invalid valid
33 '<script>' <abc.def@example.com> invalid invalid invalid invalid invalid

メールアドレスのバリデーション方法

じゃぁメールアドレスのバリデーションってどうやったらいいのさ、という話。

satob.hatenablog.com

もちろんバリデーションをした後には、メールアドレスの実在性確認のために実際にメールを送信します*1。その前段で、画面に表示したときにセキュリティ的にもレイアウト的に問題ない(ように見える)メールアドレスだけ使えるようにしておきたい。

JavaMailだけではガバガバで、Outlookのアドレス帳から取ってきたような "John Due" <john.doe@example.com> みたいなアドレスも通してしまう。これをそのまま画面表示用に使うのはさすがにマズそうなので(rtlとかltrとか入れ放題になると思う)、 john.doe@example.com の部分だけ使いたいわけです。

じゃぁJavaMailで InternetAddress#getAddress() すればアドレス部分だけ取れるからそれでよいのでは、と考えてしまうのだけれど、それもマズい。JavaMailはdomain-literal(john.doe@[203.0.113.1] みたいなやつ)を通してしまいます。domain-literalは、扱えないMUAやMTAが多いので避けたい。

こう考えると、JavaMailだけでバリデーションするのは筋が悪そうです。

一方、HTML Living Standardの正規表現は「RFC 5322にあえて準拠していないところがあるよ」と言っていますが、上記の観点から見るとそれほど悪くありません。HTML Living Standardをパスするアドレスで、JavaMailがエラーとなるものは、ローカルアドレス部分のlocal-partの先頭・末尾にドットがある場合と、連続するドットがある場合だけ *2 です。

なので、以下のようにするのがよいのではないでしょうか。

  • (A) メールを送信するMTAまで自前で制御可能で、携帯電話のキャリアメールのアドレスを利用可能にしておきたい場合はHTML Living Standardの正規表現でチェックする
  • (B) メール送信に外部のサービスを使うなどの理由で、不正なメールアドレスはトラブルの元になるから利用できなくしておきたい場合は、HTML Living Standardの正規表現とJavaMailの両方でチェックする
  • (A)(B)いずれの場合も InternetAddress#getAddress() を使う必要はなく、入力された文字列をそのままメールアドレスとして使う

*1:それ以外にアドレスの打ち間違いを検知する方法はないため

*2:MTAやMUAによっては不正なアドレスと判定される。昔の携帯電話のキャリアメールだと該当する場合がある。 https://www.rbbtoday.com/article/2009/04/03/59059.html

What does "the syntax of RFC822" means in javax.mail.internet.InternetAddress ?

Javadoc of JavaMail API says InternetAddress class uses "the syntax of RFC822". https://javaee.github.io/javamail/docs/api/javax/mail/internet/InternetAddress.html

Then how about actual implementation? Is it different from some other email validation implementation like <input type="email">?

I have tested following implementation of email address validation and compared results.

  • InternetAddress *1 class in JavaMail *2
  • <input type="email"> implementation described in HTML Living Standard 25 June 2020 *3 , /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
  • "General Email Regex (RFC 5322 Official Standard)" described in Almost Perfect Email Regex *4, (?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])
  • Regexp used in Perl正規表現雑技 *5 , /^(?:[-!#-'*+/-9=?A-Z^-~]+(?:\.[-!#-'*+/-9=?A-Z^-~]+)*|"(?:[!#-\[\]-~]|\\[\x09 -~])*")@[-!#-'*+/-9=?A-Z^-~]+(?:\.[-!#-'*+/-9=?A-Z^-~]+)*$/ (Popular in Japan)

I have used http://www.htmq.com/html5/input_type_email.shtml to test behavior of <input type="email">. Also I have used https://codepen.io/shingorow/pen/oBPZbL to test the regexps.

The results is shown in the table below.

# Test case Address JavaMail HTML Living Standard Almost Perfect Email Regex Perl正規表現雑技
1 ordinal address abc.def@example.com valid valid valid valid
2 quoted by double quote "abc.def"@example.com valid invalid valid invalid
3 quoted by double quote and contain newline + space "abc.\n def"@example.com valid invalid (*) invalid (*) invalid (*)
4 quoted by double quote and joined by "." "abc"."def"@example.com invalid *6 invalid invalid invalid
5 quoted by single quote 'abc.def'@example.com valid valid valid valid
6 quoted by single quote and contain newline + space 'abc.\n def'@example.com invalid *7 invalid (*) invalid (*) invalid (*)
7 quoted by single quote and name joined by "." 'abc'.'def'@example.com valid valid valid valid
8 contains comment with parenthesis (abc)abc.def@example.com valid invalid invalid invalid
9 contains comment with parenthesis and space (abc) abc.def@example.com valid invalid invalid invalid
10 address with angle bracket <abc.def@example.com> valid invalid invalid invalid
11 address with angle bracket and newline <abc\n .def@example.com> invalid *8 invalid (*) invalid (*) invalid (*)
12 address with angle bracket and quote <abc"def"ghi@example.com> invalid *9 invalid invalid invalid
13 address with real name foo bar <abc.def@example.com> valid invalid invalid invalid
14 address with real name (single-quoted) 'foo bar' <abc.def@example.com> valid invalid invalid invalid
15 address with real name (double-quoted) "foo bar" <abc.def@example.com> valid invalid invalid invalid
16 domain with square bracket abc.def@[example.com] valid invalid invalid invalid
17 domain with square bracket and space abc.def@[exa mple.com] invalid *10 invalid invalid invalid
18 domain with square bracket and escaped character abc.def@[exa\nmple.com] invalid *11 invalid (*) invalid (*) invalid (*)
19 domain with square bracket and quote abc.def@[example."hoge".com] valid invalid invalid invalid
20 start with comma ,abc.def@example.com valid invalid invalid invalid
21 start with semicolon ;abc.def@example.com valid invalid invalid invalid
22 contains double-dot abc..def@example.com invalid *12 valid invalid invalid
23 local address ends with dot abc.def.@example.com invalid *13 valid invalid invalid
24 local address starts with dot .abc.def@example.com invalid *14 valid invalid invalid
25 contains character outside of ASCII ⛄bc.def@example.com valid invalid invalid invalid
26 domain without TLD abc.def@localhost valid valid invalid valid
27 domain with 1 char TLD abc.def@e.c valid valid valid valid
28 domain with 2 char TLD abc.def@e.co valid valid valid valid
29 domain not compliant with RFC952 abc.def@-.com valid invalid invalid valid
30 host specified in IP address abc.def@203.0.113.1 valid valid valid valid
31 capital local address ABC.DEF@example.com valid valid invalid valid
32 tags in the double-quoted name "<script>" <abc.def@example.com> valid invalid invalid invalid
33 tags in the single-quoted name '<script>' <abc.def@example.com> invalid *15 invalid invalid invalid

(*) I could not input \n (CRLF) directly, so typed "\n" as ordinal string.

*1:https://javaee.github.io/javamail/docs/api/javax/mail/internet/InternetAddress.html

*2:https://javaee.github.io/javamail/

*3:https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address , Actual behavior of <input type="email"> on Chrome 83 is the same

*4:http://emailregex.com/

*5:http://www.din.or.jp/~ohzaki/mail_regex.htm

*6:Quote not at end of local address

*7:Local address contains control or whitespace

*8:Local address contains control or whitespace

*9:Quote not at start of local address

*10:Domain contains control or whitespace

*11:Domain contains control or whitespace

*12:Local address contains dot-dot

*13:Local address ends with dot

*14:Local address starts with dot

*15:Extra route-addr

How many fields does a class need to cause StackOverflowError with @ToString in Lombok

A: Around 568 (But sometimes it could be compile)

BeanWithSoManyFields.java · GitHub

This class causes java.lang.StackOverflowError like this:

java.lang.StackOverflowError
        at java.lang.reflect.Field.get(Field.java:393)
        at lombok.core.AST.buildWithField0(AST.java:383)
        at lombok.core.AST.buildWithField(AST.java:284)
        at lombok.javac.JavacAST.drill(JavacAST.java:357)
        at lombok.javac.JavacAST.buildStatementOrExpression(JavacAST.java:323)
        at lombok.javac.JavacAST.buildTree(JavacAST.java:183)
        at lombok.javac.JavacAST.buildTree(JavacAST.java:66)
        at lombok.core.AST.buildWithField0(AST.java:386)
        at lombok.core.AST.buildWithField(AST.java:284)
        at lombok.javac.JavacAST.drill(JavacAST.java:357)
  • You can compile this class with javac BeanWithSoManyFields.java -classpath c:\opt\pleiades\eclipse\lombok.jar if you had removed a field.

  • You can also increase the stack size of javac with -J-Xss option like javac BeanWithSoManyFields.java -J-Xss1025K -classpath c:\opt\pleiades\eclipse\lombok.jar.

  • @EqualsAndHashCode, @Getter, and @Setter do not cause StackOverflowError with this source.

  • Compiler embedded in Eclipse (ECJ) does not cause StackOverflowError with this source.