読者です 読者をやめる 読者になる 読者になる

JUnit で hamcrest のライブラリを依存関係に追加した時の SecurityException

Java JUnit

stackoverflow.com

色々自信なさげなエントリだけど一応メモ代わりという事で。

自分の場合、これが起きた原因として、一度 Maven の依存関係に JUnit が無い状態でテストクラスを作ろうとして、 「jar を追加しますか?」(うろ覚え)みたいに聞かれて Eclipse が勝手に jar をどっかのクラスパスに追加したという事があった。 それがおそらく Maven で取ってきたものとバージョンが違っててエラーになったのだと思う。

他の原因の事もあるっぽいけど、JUnit + Hamcrest + SecurityException の組み合わせが検索結果でも多いように見えるのは、それが原因の一つかもしれない。

というわけで、上記が原因の場合は一度リポジトリから取得しなおしたら直るかもしれない(プロジェクト削除の前にプッシュを忘れずに)。

最近の Java EE について調べてみた

Java Java EE

私は仕事で Java に関わってきましたが、実質仕事で関わってきたプロジェクトはまだ3~4年ぐらい、 そしてそのプロジェクトの中にはいくつかコードを書く仕事ではなく Excel 仕事がメインというプロジェクトもあり、 正直言うとあまり積極的に Java に関わろうとはしませんでした。

そんな自分が最近、自宅サーバを管理する時にちょっとした Web アプリ(というか Web GUI)がほしいと思って Java で作ろうと思って色々調べた程度のまとまらない文章なので、 以下の文章は沢山の間違いを含むと思います。よって識者の方のコメントをお待ちしています。


最近は(という程最近でもないけど)Java SE 環境でも Java EE の機能がある程度使えるようになってきた。 それを後押しした *1 のが、Spring Boot、 Dropwizard、 WildFly Swarm、Payara Micro 等で UberJAR と呼ばれる実行可能な jar ファイルにできる技術だったりする。 段々とテスタビリティやデプロイの簡便化のためにコンテナレスに向かう一方で、 サーバで動かす分には意識しなかった部分を意識しなければならなかったりする。 そうして Java SE 環境で Java EE なアプリを動かそうとしている中で、やはり Java EEJava EE Server は偉大だという事が分かってきた。

Java EE な Web アプリを作ろうとするとこんな感じのスタックになる。 Java EE はこれらの機能をセットで提供している。 これでもまだ全ての Java EE の機能を使っている訳ではない。

現在のフロントエンド開発環境並みに細かなスタックの組み合わせになっている事がわかる。

EJB抜きのJ2EEにはいったい何が残るというのだ?」と疑問に思われるかもしれない。 答えは「たくさん残るよ。」ということだ。J2EEにはEJBよりはるかにたくさんの価値が含まれている。 多くのJ2EE開発者はそのように考えてはおらず、あなたの机にこの本が置いてあったらEJB抜きのJ2EEは無意味だと言うだろう。 でも、EJBのしていることとJ2EEの全体を客観的に分析するとEJBはもっと重要な全体像のほんの一部に過ぎないことがわかる。

JavaをSEとEEに分類するのは今では無意味になってきている? - 達人プログラマーを目指して

しかし、多言語とちょっと違うのはこれらがライブラリではなく「API」だという事。 API に対する実装はいくつかあり、これがそれぞれの言葉ついての理解やレイヤー等があやふやだと「何の事を言ってんの?」ってなる原因でもある。

実際にそれぞれの API の実装の例を出すとこんな感じ(プロプラなソフトウェアに関しては出していません)。 H2 以外は全て Java EE(正確に言うと JNDI や JDBC は SE らしい)。

  • HTTP Server: Grizzly HTTP Server
  • JAX-RS: Jersey (MOXy)
  • CDI: Jersey (Weld)
    • 関連ファイル:src/main/resources/META-INF/beans.xml
  • JPA: EcliseLink
    • 関連ファイル:src/main/resources/META-INF/persistence.xml
  • JTA: Narayana
  • JNDI Server: JBoss JNP Server
    • 関連ファイル:src/main/resources/jndi.properties
  • DB: H2

上記のスタックは実際に試そうとしたけど、結局 JTA 使うのに JNDI Server が必要? *2 で、色々試したんだけど諦めて WildFly で動かした。

jndi.properties か Java でプロパティを設定すれば JNDI の lookup をサーバーではなくプロパティ値から取ってくるような事もできそうだけど、 そんな SPI (Service Provider Interface) を作ってる人はいませんでした。 これができれば jndi.properties にその設定値を書くだけで完結しそうなので、誰か作ってください。

ちなみに JavaScript の場合の事情は、これらのレイヤーは仕様として固まってない部分もあるのでレイヤーは実際曖昧だったりするけど、 JavaScript について知るにはブラウザや CSS や HTML 等その他様々な物と繋がった知識が必要になり、それらは現在進行形で開発中な所が理解を難しくしている原因だと思う。

あと Java EE のような沢山の仕様を含む仕様を「アンブレラJSR」と言うらしい。 JSR とは Python にとっての PEP とかそんな感じのもの。 PEP にあたるものは JEP というものがあるそうです。 @yy_yank さんありがとうございます! *3 仕様書は PDF で公開されておりダウンロードには Oracle の License Agreement が必要。サンキューオラクル

Java EE に含まれている各 API のバージョンと JSR は Wikipedia のページを見るのが早い。 (Java EE 開発するなら各 API のバージョンはいくつなのかを調べる事があったと思うけど、Wikipedia がここまで詳しい一覧表になっていたのは知らなかった)

Java EE version history - Wikipedia, the free encyclopedia


正直これらの事は最近という程でも全然ないんだけど、あまり追いかけてこなかった。 なぜかというと、まぁ冒頭の私の姿勢の問題が大きいけど、それに加えてやはり Java EE の機能を使うなら Java EE compliant なサーバ(製品はもっと沢山あるけど OSS なら GlassFishWildFly とか)を使った方が楽だから。

派遣エンジニアの私が参画するプロジェクトの顧客や元請けは大企業が多いだろうし、 これから仕事ではまだサーブレットエンジンを使う事になると思う。 政治面や金銭面やサポートや監視等のインフラとか諸々の部分を含めると中々環境を変えづらいというのも分かる。 よって大企業というより、これから少数精鋭で Java の Web アプリを作るような組織だったら Java EE ではないサーバ(Jetty や Grizzly 等)を使う方が適していると思う。

今思いついたメリットを挙げてみる。この他にもあると思う。

  1. 起動が速い
  2. (main() から始まるような)普通の Java アプリケーションに組込みが可能

大体テストしやすそうというのが分かるはず。

ただし、そのための条件として以下が必要となる。

  1. (必須)Java のエラーに対して深い知識を持って(上辺だけではない)対応ができる人がいる
  2. 願わくばその人や周りの環境が知識を広げて伝播してくれるような雰囲気(?)である事

この条件をクリアできるなら上掲したメリットはとても大きいと思う。 具体的にはテストや CI やモチベーション等の意味で。

Java EE に対して全ての機能を知っている人はほぼ存在しないと思われるので、 大体の人が Java EE サーバに抱く感想は「(何やってるのかわかんないけど)重くて思い通りに動いてくれない」だと思う。 自分も沢山の事をやってくれるのは何となく理解したけど、やっぱり重いのは嫌だ。 あとデプロイエラーになった時とかサーバとコードどっちに原因があるのか分からず ひたすら色んなツールのキャッシュクリアを試みた思い出とか思い出したくない。 そういったトラウマを抱えているのは自分だけではないと思うんだけど、 自分の場合 Jetty や Grizzly を起動した時の速さを目の当たりにしてちょっと克服できた。 Jetty、Grizzly かわいい。Weld(CDI)かわいい Jersey(JAX-RS)かわいいとなってようやく Java に抵抗がなくなってきた。 完全な知識理解はボトムアップでしか成しえないと思うので、 それぞれのコンポーネントを理解していると、他製品の理解にも繋がる。


tyru.hatenablog.com

これまた最近知った事で、JNDI 名はコンテナによりベース?の文字列が結構バラバラだったりする。 GlassFishjava:app/ とか Wildflyjava:jboss/ ?とか。 なので JNDI 名は省略せずフルで指定した方がいい。 ポータビリティ的な観点でフルで指定するよりも省略したほうがいいのかな、とか思ったりしたけど、それよりも環境によって変わってしまい動かなくなる事の方が問題。 まぁサーブレットエンジンを変えたら大体そのままのコードでは動かないけど、 フルで指定しているとエラーメッセージも分かりやすかったりして、特定が楽になる。

冒頭でも言ったけど、正直仕事で Java を使ってきたのに、あまり趣味では Java を使ってこなかった。 Java を趣味で使うようになったのは最近なので、こういった基礎的な事柄が抜けているなと感じる。 JNDI とかは既にプロジェクトのコードや初期の環境構築で設定済みになっている事が多い。

こういった基礎的な事柄だけど、基礎だからこそフレームワークミドルウェアによって隠される事は(Java には特に)多い気がする。 なので、自分で一から手を動かして物を作らないと分からない事って沢山あるなと痛感したのでした(小並)。

関連リンク

追記:JTA 諦めてローカルトランザクションで動かした方がいい?

jBatch RI を Java SE でまともに(JPA,CDI,Transactionalつきで)動かす #javaee - kencharosの日記

JTA@Transactional が使いたい場合は、この記事の方が書かれてるように、自分で実装しちゃうのが一番の解決策な気がします。

あと EntityManager が @PersistenceContext 経由で取れないのも Java SE 特有のあるある問題らしく、 自分も諸先輩方を見習ってプロデューサーで解決したのですが、スレッドローカルにするのは思いつきませんでした。 というか JTA 動かすのに精一杯で確かプロデューサーのクラスが @Dependant で inject 対象のクラスが @ApplicationScoped になってました。 EntityManager はスレッドセーフではないので潜在的なバグを抱えてた事になります。怖い。

*1:というよりも Java SE 環境で使う手段が大体これなので、同時に整備されてきたという理解が正しい?

*2:あと JMS を使うにも必要っぽい?使ってないけど

*3:https://twitter.com/yy_yank/status/778984326402674688 http://yyyank.blogspot.jp/2015/04/javajsr.html

GlassFish に asadmin で H2 DataSource 登録

Java Java EE

GlassFish なら管理コンソール(http://localhost:4848/)で操作もできるけど、4.1 や 4.1.1 だと JDBC に H2 の DataSource を登録する時に RuntimeError が出て登録できなかった… ので仕方なくコマンドのやり方を調べた。

コマンド

asadmin create-jdbc-connection-pool --datasourceclassname org.h2.jdbcx.JdbcDataSource --restype javax.sql.DataSource --property user=sa:password=password:url="jdbc\:h2\:mem\:test" H2Pool
asadmin create-jdbc-resource --connectionpoolid H2Pool jdbc/h2ds

失敗例

失敗例 その1

asadmin create-jdbc-connection-pool --datasourceclassname org.h2.jdbcx.JdbcDataSource --restype javax.sql.DataSource --property user=sa:password=password:url="jdbc\\:h2\\:mem\\:test" H2Pool

Windows だとバックスラッシュは1個。 cmd.exe から叩いた場合。 asadmin を実行して対話シェルから実行した場合は確認してない。

On Solaris, escape colon characters (:) within property values with double backslashes (\). On Windows, escape colon characters (:) with single backslashes ( ).

Configuring the JDBC Connection Pool (Sun GlassFish Enterprise Server v2.1.1 High Availability Administration Guide)

失敗例 その1

asadmin create-jdbc-resource --connectionpoolid H2Pool java:app/env/h2

JNDI 名は java: はつけず jdbc/h2ds のように指定する。 (先程と同じコロンの文字の制約だったかもしれない)

To Create a JDBC Connection Pool (Oracle GlassFish Server 3.0.1 Administration Guide)

すると java:app/jdbc/h2ds という JNDI 名になる。

ちなみに

GlassFish 4 系でいろいろ頑張ってみたものの、エラーが出たりバグが多いから 3 に戻したよ!って言ってるページがあって、 3 に戻したら Java SE 7 + Java EE 6 を強要されてどうせなら新しい技術触ってみたいし実際に Java SE 8 + Java EE 7 で書いてしまってたので WildFly にした。

Windows のリソースモニター便利


一応画像はブログにもアップロードしておく(Twitter の永続性を信用してない)。

f:id:tyru:20160922192748p:plain

業務アプリで malloc() のアルゴリズムを実装する羽目になるとは思わなかった話

釣りタイトルだけど嘘は言ってない。

DBから連続的な番号をN個取る処理は考え方としては malloc() と同じだと気付いて Twitter でアレコレ考えたのをまとめた。

要点だけ言うと、

  • 連続的な番号をN個取得し、それを ID として貸し出す処理は(考え方としては)malloc() と同じ
  • チーム開発で高度な技術を使ったら負け(誇張あり)
  • 飲酒は程々にしましょう

etckeeper で commit されたら GitLab に push する

手順 (各ホスト)

以下の手順を自動化したスクリプトはこちら(実際のものとは多少異なります)。

etckeeper-setup-host.sh · GitHub

1. SSH鍵を生成

# ssh-keygen -t rsa -C "$(hostname)@gitlab-root.url" -N '' -f /root/.ssh/gitlab-$(hostname)

2. /root/.ssh/config に以下を追記

Host gitlab-root.url
    User (ホスト名)
    IdentityFile /root/.ssh/gitlab-(ホスト名)

3. gitlab-root.urlSSH 鍵を /root/.ssh/known_hosts に登録する

自動化されたスクリプトでは ssh-keyscan コマンドを使っている(関連:非対話的に ~/.ssh/known_hosts を更新 - Humanity)。

# ssh gitlab-root.url

4. リポジトリの初期化

# etckeeper init

5. GitLab の remote URL を追加

# etckeeper vcs remote add gitlab git@gitlab-root.url:etckeeper/etckeeper-$(hostname).git

6. 設定ファイルの PUSH_REMOTEgitlab を追加

# vim /etc/etckeeper/etckeeper.conf

# To push each commit to a remote, put the name of the remote here.
# (eg, "origin" for git). Space-separated lists of multiple remotes
# also work (eg, "origin gitlab github" for git).
PUSH_REMOTE="gitlab"

手順 (GitLab の Web GUI から)

こっちの手順も自動化したい。 たくさんコマンドラインツールがあるみたいだけどあんまり調べてない。そのうちやるかも。

  1. 各ホストのユーザを作成
  2. ログアウトした状態のトップページでアカウントを作成すれば、パスワードも一緒に設定できる
  3. Admin Area からだとメール記載の URL から設定する必要がある
  4. ユーザのSSH鍵を追加 (各ホストごとの手順で作成)
  5. 各ホストのリポジトリを作成
  6. 各ホストのユーザを etckeeper/{ユーザ名} のプロジェクトに Master として追加

以上

これで /etc/cron.daily/etckeeper が実行されるたびに git push される。

/etc/ssh/ssh_host_* の SSH 鍵を再生成する

d.hatena.ne.jp

結論から言うと、上記記事の通り SSH 鍵を削除するなり移動するなりして sshd を再起動すれば自動的に生成してくれる。

# rm -f ssh_host_{ecdsa,ed25519,rsa}_key{,.pub}
# systemctl restart sshd

どんな風に実装されてるか気になったので調べた。のでまとめる。 CentOS 7 上だと以下の Unit が生成処理を行っている(終)。

/usr/lib/systemd/system/sshd-keygen.service

[Unit]
Description=OpenSSH Server Key Generation
ConditionPathExists=|!/etc/ssh/ssh_host_rsa_key
ConditionPathExists=|!/etc/ssh/ssh_host_ecdsa_key
ConditionPathExists=|!/etc/ssh/ssh_host_ed25519_key
PartOf=sshd.service sshd.socket

[Service]
ExecStart=/usr/sbin/sshd-keygen
Type=oneshot
RemainAfterExit=yes

通常はやることがないのでこんなログになる。

$ sudo systemctl status sshd-keygen.service
● sshd-keygen.service - OpenSSH Server Key Generation
   Loaded: loaded (/usr/lib/systemd/system/sshd-keygen.service; static; vendor preset: disabled)
   Active: inactive (dead)
Condition: start condition failed at 日 2016-09-04 02:13:33 JST; 16h ago
           none of the trigger conditions were met

 9月 04 02:13:33 myhost systemd[1]: Started OpenSSH Server Key Generation.

キーを移動して sshd-keygen.service を再起動してみる。

$ mkdir -p sshkeys.old; for i in ssh_host_{ecdsa,ed25519,rsa}_key{,.pub}; do sudo mv /etc/ssh/$i sshkeys.old/$i; done
$ ls sshkeys.old/
ssh_host_ecdsa_key  ssh_host_ecdsa_key.pub  ssh_host_ed25519_key  ssh_host_ed25519_key.pub  ssh_host_rsa_key  ssh_host_rsa_key.pub
$ sudo systemctl restart sshd-keygen.service

そうするとこうなる。

$ sudo systemctl status sshd-keygen.service
● sshd-keygen.service - OpenSSH Server Key Generation
   Loaded: loaded (/usr/lib/systemd/system/sshd-keygen.service; static; vendor preset: disabled)
   Active: active (exited) since 日 2016-09-04 18:32:28 JST; 3s ago
  Process: 2417 ExecStart=/usr/sbin/sshd-keygen (code=exited, status=0/SUCCESS)
 Main PID: 2417 (code=exited, status=0/SUCCESS)

 9月 04 18:32:28 myhost systemd[1]: Starting OpenSSH Server Key Generation...
 9月 04 18:32:28 myhost sshd-keygen[2417]: Generating SSH2 RSA host key: [  OK  ]
 9月 04 18:32:28 myhost sshd-keygen[2417]: Generating SSH2 ECDSA host key: [  OK  ]
 9月 04 18:32:28 myhost sshd-keygen[2417]: Generating SSH2 ED25519 host key: [  OK  ]
 9月 04 18:32:28 myhost systemd[1]: Started OpenSSH Server Key Generation.

今回は sshd-keygen.service を restart したけど、sshd.service は sshd-keygen.service に依存しているので systemctl restart sshd.service しても再生成されるのを確認した。 ので、普通は sshd.service を restart すればいい。

ちなみに sshd は restart しても既存の接続が切れることはない。詳しくは以下の記事を参照。

equj65.net