タイガー!タイガー!じれったいぞー!(SE編)

AS400,WAS,GlassFish,Java,JavaEE,JSF等の開発における日々の気づきをまとめたブログ(備忘録)。

【NetBeans】文字コードの設定

PC移行に伴い、NetBeansを新たにセットアップしたのですが、文字化けに苦しんだので、現時点で上手くいった設定を記録しておこうと思います。

私の環境(2017-06-29)

  • OS: Windows10 Pro x64 anniversary update
  • Java: 1.8.0_112
  • NetBeans: 8.2
  • Maven: 3.0.5(バンドル版)
  • JavaServer : Payara 4.1.1.163

設定手順

Serverのコンソールログ、並びにMavenの実行結果の両方を「UTF-8」で出力させる設定になります。

(1) PC環境変数設定

  • 結論から言いますと、結局、この設定だけで文字化け問題は解決しました。
  • ユーザー環境変数に「JAVA_TOOL_OPTIONS」を追加します。
変数名: JAVA_TOOL_OPTIONS
変数値: -Dfile.encoding=UTF-8

f:id:no14141:20170629180328j:plain

(2) NetBeans設定

  • NetBeansでは、起動オプションを設定ファイルでの編集が可能です(私の保存場所:C:\Program Files\NetBeans 8.2\etc\netbeans.conf)。
  • 以前は、「netbeans_default_options」に「-J-Dfile.encoding=UTF-8」を追記していたはずでしたが、改めて前のPCの設定を確認してみると、設定は存在しませんでした。ちなみに「-J-Dfile.encoding=UTF-8」を追記すると、Mavenの出力が文字化けしました。
  • 結局こちらには、jdkhomeの設定のみとしました。
netbeans_jdkhome="C:\Program Files\Java\jdk1.8.0_112"

以上でPayaraへのログ、並びにIDE上で実行するMavenの出力は、すべて文字化けしないようになりました。

「JJUG CCC 2017 Spring」に行ってきました #jjug_ccc

2017年5月20日にJJUGに行ってきました。 トータルこれで4回目。2016年春からは3回連続で参加させていただいております。

今回はJava基本的なコーディング力とDB関連の技術情報を得ることが目的。
毎回、JJUGのあとは、何か新しいことを取り入れているので、今回もぜひとも実現したいところ。

www.java-users.jp

日本Javaユーザーグループ 年次総会

それにしてもすごい人。20代の若者たちが多く参加しているようで。40代の私ですが、好奇心は若者たちと同じくらいあると思いたい!
毎回思うのですが、コミュニティ運営者の方々には、感謝しかありません。
B/S、P/Lを拝見するのも、春のお楽しみのひとつ。

来年は何と10周年イベントを開催予定だとか!! これは楽しみが1つ増えましたね!

ふつうのJavaコーディング

発表者のirofさんからは、「みなさん、部屋、間違えてません?」と一言。立ち見の方が多く出るほど大盛況でした。
今回の1つの目的、Java基礎力アップ。そのヒントをたくさんいただけたセッションでした。
「コードは読み物」「引数を少なくする」「名前で表現する」などなど。なかでも名言だったのが「わざわざ作ると向き合う」という言葉。動けばいい、動いたからオッケー!という日もこれまで何度もありました。意識を変えて、今後はこのセッションで学んだことを取り入れて、自分のコードを少しでも美しくしていきたいと思いました。
まず、すぐにでも実践しようと思ったことは、enumにメソッドを持たせること。これまでは、ただの値を列挙していただけだったので、これならすぐにでもトライできそうです。

Vue.js + Spring Bootで楽しくフルスタック開発やってみた

今後の開発をJSFからJS系に切り替えていこうと考えている最中なのですが、いったい、どのJSフレームワークがいいのか? 毎日のように悩んでおりましたが、まずは実際にやっている人の声を聞きたいということで、こちらのセッションを選択しました。
Vue.jsのお話が聞けました。AngularとReactの2強と思っていたのですが、このフレームワークも評判がいいようです。学習コストがあまりかからないという話も。
Vueの話だけではなく、Selenide、CSS Grid Layoutもおすすめとか。どちらも触ったことがないため、イメージがわかず。自分の手をトコトン動かすしかありませんね。
Javaデザインパターンでは、「Observer」「Iterator」「Visitor」あたりはとても重要とのことで、あたらめて復習する必要がありそうです!

SpotBugs(FindBugs)による大規模ERPのコード品質改善

当初は、Postgresのセッションに出る予定でしたが、直前にこちらに変更! 「ふつうのJavaコーディング」を聞いて、より品質改善に意識が向いたからこそです。
FindBugsって、開発が止まっていたのですね。性的解析ツールの基本をこちらでは、教わりました。
SonarQubeを試したいというテーマも1年以上放置しているので、こちらについてもJenkinsで実行できるように挑戦しようと思っています。

Javaエンジニアに知って欲しいRDBアンチパターン

個人的には、こちらのセッションで最後(この後、マインドフルネスの体験の予定を入れていたため)。
最後にふさわしい熱気のある素晴らしいセッションでした。DBのアンチパターンの話、少しずつリファクタリングしていくという話、いずれも目から鱗でした。ちょうど、購読中の雑誌「ソフトウェアデザイン」に壮大さんの記事が連載中なので、合わせてきちんとチェックしたいところです(すでに始まっているのに、まだ読めていません。。。) 印象に残った言葉も多くいただきました。「データベースの死はサービスの死」「データベース問題を解決できる人は英雄」「DBの問題は忘れた頃にやってくる」「RDBの知識は寿命が長い。覚えれば仕事で長い間役に立つ」、特にコップからあふれた水の話がわかりやすかった!
これを聞いて、今使っているRDBの監視を強化しよう!と強く思った次第です。


今回はJavaEE関連のセッションには、参加しませんでした。
いよいよ、今年夏以降には、新しいSEとEEが登場してきます。検証はする予定ですが、まずは現在開発中のアプリを安定して動かすことが当面の目標なので、今回学んだことを今の開発に持ち込んで、今よりもちょっとでも良くしていけたらいいなと思っています。
そのためにも、はてなの大西さんの言葉「手を動かした人だけが世界を変える」を教訓とし、手を動かし続けていく必要がありそうです!!

【AS400】RPGLE-MOVEA命令をfreeでどう書くか

最近、RPGLEはほぼFree形式で書くようにしています。

しかしながら、MOVEA命令のfree置き換えが無いっぽいので、困っておりました。

MOVEA命令は、標識の一括セット/クリアなどで便利だったのですが・・・。

サンプル1

標識30~59までを一括でOFFに設定する例です。

C                   MOVEL     *ALL'0'       W@FLGS           30 
C                   MOVEA     W@FLGS        *IN(30) 

そこで、仕方なくFOR文回し(サンプル2)でクリア処理を行っていましたが、BIF関数の%SUBARRを使えば、1行で書けることがわかったので、備忘録(サンプル3)として残します。

サンプル2

 /FREE 
  FOR INT=30 TO 59;            
      *IN(I) = *OFF;         
  ENDFOR;
 /END-FREE  

サンプル3

 /FREE
  %SUBARR(*IN:30:30) = *OFF;   //標識30番目から30個分(*IN30~59)
 /END-FREE  

残る課題は、サブファイル操作なので行う異なる値セットを一括でどうやるかです。
%SUBARRでは、'100'とか'011'をセットすることができません。
現状、標識セット命令を3行書いております。。。(汗)

Payara Serverアップグレードを試す

Java EE Advent Calendar 2016の16日目です。

昨日の記事は @khasunumaさんの「JavaFX から Payara Micro API を呼び出す際の注意点」でした。
明日は@backpaper0@githubさんです。



今回のゴール

  • 既存のアプリが起動しているJavaEE環境(CentOS7.2)のアップグレードを行う。
    • JDK 8.45 => JDK 8.112
    • Payara 4.1.1.154 => Payara 4.1.1.164

目的

去年の11月より、業務系サーバーに Payara を選定し、ようやくPayara管理者2年生を迎えました。
実際、運用中のPayara Severは、利用ユーザーも少なく、オンプレミス環境1台で働いてくれています。

Payaraは、3か月ごとに新しいフルリリース(バグフィックスと新機能)を提供してくれていますが、リリースしたものが安定稼働していると、なかなかアップグレードをしなくなってしまいます。

そこで、現在動いているシステムのPayaraとJavaのアップグレードをテスト機で試しておこうと思ったのが、この記事を書くきっかけとなりました。
正直、JavaEEの原理・原則を正しく理解できていないので、作業はコピペ中心です(汗)。。。

作業手順

※注:下記Linuxコマンドは、一時的な検証機での操作のため、root権限で行っています。

(1) JDKアップグレード

JDKダウンロード

現在運用のJDKは、8.45であるため、こちらも最新版にしておこうと思います。
Oracleのdownloadsサイトより、CentOS7用のlinux-x64.rpmをダウンロードします。

JDKインストール

作業前Version確認

まずは、現状確認から。1.8.0_45で間違いないようです。

# java -version
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)

# javac -version
javac 1.8.0_45
ファイル配備

とりあえず、/tmpへ配置しました。

# ls /tmp
jdk-8u112-linux-x64.rpm
インストール

rpmコマンドでインストール開始です!

# rpm -Uvh jdk-8u112-linux-x64.rpm
準備しています...              ################################# [100%]
更新中 / インストール中...
   1:jdk1.8.0_112-2000:1.8.0_112-fcs  ################################# [100%]
Unpacking JAR files...
        tools.jar...
        plugin.jar...
        javaws.jar...
        deploy.jar...
        rt.jar...
        jsse.jar...
        charsets.jar...
        localedata.jar...

無事、インストールが終わったようです。

Java実行環境切替

J実行環境切替は、不要でした。
すでに、インストールしたばかりの「jdk1.8.0_112」が選択されているようです。

# alternatives --display java
java -ステータスは自動です。
リンクは現在 /usr/java/jdk1.8.0_112/jre/bin/java を指しています。 <<<<<<<<<<<<<<<<
/usr/java/jdk1.8.0_45/jre/bin/java - 優先項目 18045
 スレーブ ControlPanel: /usr/java/jdk1.8.0_45/jre/bin/ControlPanel
 スレーブ javaws: /usr/java/jdk1.8.0_45/jre/bin/javaws
:
:
/usr/java/jdk1.8.0_112/jre/bin/java - 優先項目 180112
 スレーブ ControlPanel: /usr/java/jdk1.8.0_112/jre/bin/ControlPanel
 スレーブ javaws: /usr/java/jdk1.8.0_112/jre/bin/javaws
:
:
現在の「最適」バージョンは /usr/java/jdk1.8.0_112/jre/bin/java です。
作業後Version確認

Version確認でも、1.8.0_112となっています。

# java -version
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, mixed mode)

# javac -version
javac 1.8.0_112
切替が必要な場合

もし、新しいVersionのJDKが適用されていなければ、「alternatives –config java」でjavaを指定できます。また、業務アプリが正常に動かないなど、やむを得ず戻す必要になった場合には、このコマンドで前のVersionに戻すことが可能です。

# alternatives --config java

2 プログラムがあり 'java' を提供します。

  選択       コマンド
-----------------------------------------------
   1           /usr/java/jdk1.8.0_45/jre/bin/java
*+ 2           /usr/java/jdk1.8.0_112/jre/bin/java

Enter を押して現在の選択 [+] を保持するか、選択番号を入力します: 

④Payara起動確認

PayaraのWeb管理コンソールにアクセスし、「JVM Report」を確認すると、「jdk1.8.0_112」で起動していました。
オッケー、オッケー!!

java.home = /usr/java/jdk1.8.0_112/jre
java.runtime.name = Java(TM) SE Runtime Environment
java.runtime.version = 1.8.0_112-b15

(2) Payaraアップグレード

まず、今回わかったこと。
Payaraのリリース番号って、西暦下2ケタ+その年の回数ってことなんですね! はじめて利用したとき、154個目のリリースだと思ってしまいました。。。なので、現時点の最新版は164。
2016年4番目のリリースにアップグレードをしましょう。

参考にしたサイトは、こちらになります。

最初は、Web管理コンソール画面でアップグレードメニューなどあるのかなと思っていたのですが、基本的な移行方法として下記の2つが選択肢になるようです。

  1. 既存の構成をバックアップして新しいインストールに復元する
  2. 完全に分離したドメインとノードのディレクトリを維持し、新しいバージョンを既存のディレクトリに向ける

今回は、1.の方法でアップグレードの検証を行います。

①起動中Payaraの停止(payara154)

まずは、動いているPayaraサービスを止めます。
このPayaraサービスは、後述の⑦payara.service で定義済みです。

# systemctl stop payara  (/opt/payara/payara-4.1.154/payara41/bin/asadmin stop-domain)

ドメインのバックアップ(payara154)

現行のドメインドメイン名:domain1)をバックアップします。
バックアップ先は、「/opt/payara/backups」としました。
終了すると、バックアップ先に「domain1/domain1_2016_12_13_v00001.zip」というファイルが出来上がっていました(当作業は、2016/12/13に実施)。

# /opt/payara/payara-4.1.154/payara41/bin/asadmin backup-domain --backupDir /opt/payara/backups domain1
Backed up domain1 at Tue Dec 13 19:39:59 JST 2016.
Command backup-domain executed successfully.

# ls /opt/payara/backups/domain1
domain1_2016_12_13_v00001.zip

③環境構築(payara164)

次に、新環境構築です。

ファイル配備

Payaraのダウンロードページより、現時点で最新の「payara-4.1.1.164.zip」をダウンロードし、「/opt/payara/payara-4.1.164」へ配備しました。

# mkdir /opt/payara/payara-4.1.164

//ダウンロードしたファイルを配備する。

# ls /opt/payara/payara-4.1.164
payara-4.1.1.164.zip

続けて解凍処理を行います。

# unzip payara-4.1.1.164.zip
# ls /opt/payara/payara-4.1.164
payara-4.1.1.164.zip  payara41

ドメインのリストア(payara154→payara164)

②で作成されたバックアップファイルを新環境へリストアします。
バックアップディレクトリとドメイン名を指定して、無事、リストアが成功しました!!

# bin/asadmin restore-domain --backupdir /opt/payara/backups domain1
Restored the domain (domain1) to /opt/payara/payara-4.1.164/payara41/glassfish/domains/domain1
Command restore-domain executed successfully.

ドメイン所有者変更(payara164)

rootで操作してきたため、新Versionのpayara内、リストアされたドメインすべてが所有者rootになっているため、これをすでに作成済みユーザーの「payara」へ変更します。
このpayaraユーザーがpayaraサービスの実行ユーザーとなっているため、この変更は必須となります。

# chown -R payara:payara /opt/payara/payara-4.1.164

⑥起動確認(payara164)

さあ、起動してみましょう!!(テスト機 IP Address: 192.168.100.100)

# /opt/payara/payara-4.1.164/payara41/bin/asadmin start-domain
Waiting for domain1 to start ................................
Successfully started the domain : domain1
domain  Location: /opt/payara/payara-4.1.164/payara41/glassfish/domains/domain1
Log File: /var/log/payara/server.log
Admin Port: 4848
Command start-domain executed successfully.
  • https://192.168.100.100:4848

20161213193717

無事に起動できました!
当たり前なのかもしれませんが、前の設定もすべて引き継がれており、設定変更は不要でした。

※ログファイルの保管場所が同一ドメイン内である場合は、修正が必要でしょうか。

payara164停止→payara154起動テスト

念のため、payara154が動作するかもテスト。まったく問題ありません。

# /opt/payara/payara-4.1.164/payara41/bin/asadmin stop-domain
Waiting for the domain to stop .
Command stop-domain executed successfully.

# /opt/payara/payara-4.1.154/payara41/bin/asadmin start-domain
Waiting for domain1 to start ...................................
Successfully started the domain : domain1
domain  Location: /opt/payara/payara-4.1.154/payara41/glassfish/domains/domain1
Log File: /var/log/payara/server.log
Admin Port: 4848
Command start-domain executed successfully.

# /opt/payara/payara-4.1.154/payara41/bin/asadmin stop-domain
Waiting for the domain to stop .
Command stop-domain executed successfully.

なお、154と164を複数起動してみると、同じ設定のpayaraなので、ポートが被って後者が起動できませんでした。

# /opt/payara/payara-4.1.154/payara41/bin/asadmin start-domain
There is a process already using the admin port 4848 -- it probably is another instance of a GlassFish server.
Command start-domain failed.

⑦payaraサービスの編集

単一コマンドで起動/停止を確認できたので、payaraサービスの設定も変更しておきます。
「payara-4.1.154」の箇所を「payara-4.1.164」へ変更。

/lib/systemd/system/payara.service

[Unit]
Description=Payara Server
After=network.target remote-fs.target

[Service]
Type=forking
### payara-4.1.154 => payara-4.1.164
PIDFile=/opt/payara/payara-4.1.164/payara41/glassfish/domains/domain1/config/pid
ExecStart=/opt/payara/payara-4.1.164/payara41/bin/asadmin start-domain domain1
ExecReload=/opt/payara/payara-4.1.164/payara41/bin/asadmin restart-domain domain1
ExecStop=/opt/payara/payara-4.1.164/glassfish41/bin/asadmin stop-domain domain1
TimeoutStartSec=300
TimeoutStopSec=30
User=payara

[Install]
WantedBy=multi-user.target

systemdへ設定反映

# systemctl daemon-reload

⑧サービスでの起動・停止

payaraサービスで上げ下げしてみます。こちらも問題なしでした。

# systemctl start payara

# systemctl status payara
● payara.service - Payara Server
   Loaded: loaded (/usr/lib/systemd/system/payara.service; enabled; vendor preset: disabled)
   Active: active (running) since 火 2016-12-13 19:53:21 JST; 5s ago
  Process: 29867 ExecStop=/opt/payara/payara-4.1.164/glassfish41/bin/asadmin stop-domain domain1 (code=exited, status=203/EXEC)
  Process: 29894 ExecStart=/opt/payara/payara-4.1.164/payara41/bin/asadmin start-domain domain1 (code=exited, status=0/SUCCESS)
 Main PID: 29908 (java)
   CGroup: /system.slice/payara.service
           mq29908 /usr/java/jdk1.8.0_112/bin/java -cp /opt/payara/payara-4.1.164/payara41/glassfish/modules/glassfish.jar -XX:+UnlockDiagn...

12月 13 19:52:48 localhost.localdomain systemd[1]: Starting Payara Server...
12月 13 19:53:20 localhost.localdomain asadmin[29894]: Waiting for domain1 to start ...............................
12月 13 19:53:20 localhost.localdomain asadmin[29894]: Successfully started the domain : domain1
12月 13 19:53:20 localhost.localdomain asadmin[29894]: domain  Location: /opt/payara/payara-4.1.164/payara41/glassfish/domains/domain1
12月 13 19:53:20 localhost.localdomain asadmin[29894]: Log File: /var/log/payara/server.log
12月 13 19:53:20 localhost.localdomain asadmin[29894]: Admin Port: 4848
12月 13 19:53:20 localhost.localdomain asadmin[29894]: Command start-domain executed successfully.
12月 13 19:53:21 localhost.localdomain systemd[1]: Started Payara Server.

# systemctl stop payara

⑨業務アプリの動作確認

基本的なJavaEEアプリ「JSF(PrimeFaces)+CDI/EJB+JPA」で作られたWeb業務システムの動作検証を行いました。全テストは実現できておりませんが、問題なく動作できていることを確認できました。

完全テストを終えたのち、本番環境の方もJDK/Payaraのアップグレードを実施しようと思います。

これにて、今回の検証は終了となります。

まとめ

今回のpayaraのアップグレードで、主なモジュールも下記の通り、Versionアップされました。

modules 4.1.154 4.1.161 4.1.162 4.1.163 4.1.164
Eclipselink 2.6.1 2.6.2 2.6.3
Grizzly 2.3.23 2.3.24 2.3.25 2.3.28
Hazelcast 3.5.2 3.6.2 3.6.4 3.7.1
Jackson 2.5.1 2.8.1
Jersey 2.22 2.22.1 2.22.2
Mail 1.5.4 1.5.6
Mojarra 2.2.12 2.2.13
Tyrus 1.11 1.13
Weld 2.2.16.Final 2.3.2.Final 2.3.5.Final 2.4.0.Final

Payara164で起動すると、EclipseLinkのVersionも上がったことで、今まで起動時に毎回発生していた下記のヌルポも発生しなくなりました。

java.lang.NullPointerException
    at org.eclipse.persistence.platform.server.ServerPlatformUtils.createServerPlatform(ServerPlatformUtils.java:99)
    at org.eclipse.persistence.sessions.factories.SessionManager.init(SessionManager.java:77)
    at org.eclipse.persistence.sessions.factories.SessionManager.<clinit>(SessionManager.java:71)
    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.addSessionToGlobalSessionManager(EntityManagerSetupImpl.java:907)
    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.initSession(EntityManagerSetupImpl.java:2671)
    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:675)
    at org.eclipse.persistence.internal.jpa.EntityManagerFactoryDelegate.getAbstractSession(EntityManagerFactoryDelegate.java:205)
    at org.eclipse.persistence.internal.jpa.EntityManagerFactoryDelegate.createEntityManagerImpl(EntityManagerFactoryDelegate.java:305)
    at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.createEntityManagerImpl(EntityManagerFactoryImpl.java:337)
    at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:318)
    at com.sun.enterprise.container.common.impl.EntityManagerWrapper._getDelegate(EntityManagerWrapper.java:197)
    at com.sun.enterprise.container.common.impl.EntityManagerWrapper.find(EntityManagerWrapper.java:341)
:

今回はじめて、JavaEEのサーバーのアップグレードに挑戦したわけですが、やはり最新版で動いているという安心感があります。
もちろん、ケースバイケースかと思いますが、今後も、可能なものについては積極的に新しいVersionへ上げていきたいと思っています。

「JJUG CCC 2016 Fall」に行ってきました #jjug_ccc

日付を跨いでしまいましたが(長州力に「跨ぐなよ!、おいコラ!」と恫喝してもらいたい)、昨日、新宿で行われたJJUGに行ってきました。
2015年春、2016年春に続いて3回目。急で強引な参加になってしまいましたが、Javaエンジニア力アップのためにも、チーム開発レベルアップのためにも、何としても次の行動に結びつけるための何かを持ち帰りたい!!! そんな思いで今回も参加しました。

www.java-users.jp

基調講演1 Be a great engineer!〜 フォローすべきトレンド、スルーすべきトレンドをどう見抜くのか

まずは、朝一発目! 去年のJavaDayで初めて講演を聞かせていただいた谷本さんの話。
前回同様、お笑い満載かと思っていたら、哲学的なお話。確かに先を見越して何にウォッチすべきかってことは、7つの習慣でいうところの第2領域の時間の使い方。的外れな技術を選んでいると、結果的に緊急性の高いタスクに振り回されることにもなります。
今自分は、JSF(スルー対象)で開発をしていますが、自分たちのニーズにまさにマッチしているので、これはこれでオッケーだと思われますが、Javaフレームワークに縛られずに、同じコンポーネントベースのAngularなどのJSフレームワークも含めて3歩先を見ていこうと思っています。まあ、その前にJFS2をまだ極めきれていないので、まずはこちらを優先する必要があります。

基調講演2 Java EE - What's Next?

Oracle社のAnil Gaurさんの講演。今後のJavaEEの展望のお話。
時代は、急速にマイクロサービス化に向かっていく(いる)、そこにJavaEEもフォーカスしていくという概要か。。。
今年春から英語の勉強を再開して継続しているのですが、ほぼ聞き取れず。通訳の方、ITにも精通しているのでスラスラ翻訳し、凄すぎです! 当然、JavaEE8,9のリリースも気になりますが、次回以降、自分の英語リスニング力をアップして、本場の方の声を堪能したいところであります。

メンバーのスキルアップ、どうしてる? -Java 100本ノックで新加入メンバーを鍛えてみた-

メンバーのJava力アップに「Java100ノック」を使ってきたお話。メンバーのスキルアップには、レビューが最も大切という。さらに、レビューは、間違いを指摘するだけでなく、「よくできたことを伝える」という部分が目から鱗でした(その前に定期的なレビューができていないのですが・・・)
さらに、アンチパターンに名前を付ければいいよと。これは今日から実践できることで、自分が経験した失敗事例アンチパターンJavaに限らず)を命名しようと思います。さて、どんな風に名前を付けようか? どうせなら、覚えやすいセンスあふれる名前がいいですよね。

GitBucketを支えるJava技術とグローバルで使われるOSSの作り方

社内で、バリバリ活用させてもらっているGitBucketの歴史を聞くことができました。今回、自分が一番楽しみにしていたセッション。
GitBucketの産みの苦労話で、「こういうプロダクトを作ってみようと思う」という著者に対し、仲間から「そんなの無理だろう」というストーリー。ここで実際に優れたプロトタイプを制作し、メンバーにみてもらって「これはいける!」となったそうです。反対などにも目をくれず、行動していく姿勢があったからこそ、このOSSがあるということですね。
増井雄一郎さんの「wri.pe」同様に、まずは自分が欲しいものを創る! その精神が素晴らしいです。
自分が今一番欲しい道具っていったい何だろうか? そういう自分への問いが大切なのかもしれません。「今までこうやってきたから!」などという思考停止発言はしたくないですしね。

JVMのトラブル解決のためにやったこと~メモリー/スレッド

こちらもずっと悩ませているヒープについて。JVM、メモリ、GC、スレッドの基本の話の後、実際にやったトラブルシューティングの話がとても参考になりました。大事なのは、問題が起きてからではなく、正常時の定期観測というもの。そのツールとして、Zipkin(分散トレーシングシステム)、cactiLinuxリソース監視Tool)、Sentry(エラートラッカー)なども教えていただきましたが、まずは、はやりログ監視をしっかり実現していく必要がありそうです(できていないので)。そのツールは定番の「kibana+fluentd+elasticsearch」を習得していきたいですね。

JPA と DDD の関係で僕が思っていること

キータの「JavaEE使い方メモ」を何度も拝見させていただいています。そのページを書いている方がスピーカーということで、こちらも楽しみにしていたセッション。
自分が抱いているJPA像って、「複雑で、いまいち」というもの。その悪印象を払拭していただくことができました。JPAどうのこうのの前に、やはり大切なのはデータ構造であるということを改めて思い知る良い機会となりました。
RDB上の再設計はまだ先になりそうですが、最重要課題であることは間違いないので、今からJPAと合わせて準備します。

Payara Micro の設計と実装

組み込み系Payaraについてのお話。正直、フルPayaraを使うようになって、もうすぐ1年か。。。
Glassfishと比べてかなり安定しており、定期的にアップデートしてくれるPayara。近々、最新版にアップする計画もあるので、Payaraの中身について話を聞けてとても良かったです。Bootstrapの部分や停止する仕組みなど、そういう内部的な動きについて詳しくなることで、トラブル解決力も上がっていくでしょう。


2016年春は、JavaEE系の話がほとんどなかったのですが、今回はJavaEE系のセッションは多くあり、とても参考になりました。ただし、EEよりもSpringの方が人気と熱気があるように思えました。Springについては、まったく試していないので、こちらも自習から初めてみます。 合わせて、JPA活用、Javaスキルアップ(教育)、ヒープ管理、ログ監視とまだまだできていないことがたくさんあることを認識することができた良い機会となりました。

できる範囲で、いや無理をしてでも、ひとつずつ取り入れてやっていこうと思っています。

何のためにそれらをやるのか? 答えは簡単です。
そのシステムを使うユーザーのため。さらに継続的に管理・改善していく自分たちチームのため。その2つしかありませんね!

【AS400】レコードロック検証

改めて、DB2 for iでのレコードロックを検証していきたいと思います。

(1) レコードロックとは?

レコードロックとは、ローリングストーンズ、ビートルズ、ジミヘンなどのLPのことで・・・違いました。それはロックレコードですね。。。
テーブルのとある行をこれから書き込みを行う作業前に、その対象レコードを「今から俺が使うよ!」という宣言のようなもので、レコード(行)をロック(占有)することです。
これにより、ロックされたレコードは、他のユーザーやバッチプログラムなどからの更新処理を一時的にブロックすることができます。
書き込み直前にロックをし、レコードの値を更新して対象レコードを更新する。そんな流れになります。
ただし、ロック事態が排他制御をしてくれるわけではなく、排他制御はプログラムできちんと実装する必要があります。(JavaJPAでは、悲観的ロックや楽観的ロックという手法もあります)

(2) RPGプログラムでレコードロックを発生させてみる

それでは、実際にレコードロックを発生させてみましょう。

サンプルで下記のようなテーブルを準備しました。従業員マスタは、レコードロック検証全体で使います。部門マスタはデッドロック検証で利用します。

■ 従業員マスタ (TIGERDB/EMPLOYEES)

社員CD 苗字 名前 電話番号 部門CD
0001 山田 太郎 012-3456-7890 10
0002 山田 花子 012-3456-7891 20

■ 部門マスタ (TIGERDB/SECTIONS)

部門CD 部門名
10 営業部
20 管理部

次のような対話型RPGを作成しました。1つの対話型JOBで「社員CD=0001」をロックし、もう1つの対話型JOBで同じ「社員CD=0001」をロックしてみます(検証1の図参照)。
ちなみに、ロック呼び出しの条件は、対象テーブル(EMPLOYEES)をUモードで定義し、READ、CHAIN文などでオプション(N)を付けずに実行すれば、ロック読み取りが実施されます!

f:id:no14141:20161121170325j:plain

検証1用ソースPGM RPGLESRC/SAMPLE39

H DATEDIT(*YMD/)                                                 
F****************************************************************
F* FILE 定義                                                     
F****************************************************************
F* 従業員マスタ                                                  
FEMPLOYEES UF   E           K DISK                               
F*                                                               
D****************************************************************
D* PROTOTYPE/INTERFACE                                           
D****************************************************************
D MAIN            PR                  EXTPGM('SAMPLE39')         
D MAIN            PI                                             
D*                                                               
D****************************************************************
D* VARIABLE 定義                                                 
D****************************************************************
D LOCK_ID         S              4A   INZ(*BLANK)                
D UNLOCK_FLG      S              1A                              
D W@MSSG          S             50    INZ(*BLANK)                
D*                                                               
C****************************************************************
C* MAIN                                                          
C****************************************************************
C                   EXSR      #MAIN                              
C                   EXSR      #FINAL                             
 /FREE                                                           
  //*************************************************************
  //*  MAIN                                                      
  //*************************************************************
  BEGSR #MAIN;                                                   
     DSPLY ' レコードIDを指定して下さい  ?' '' LOCK_ID;
     EMP_CD = LOCK_ID;
     CHAIN EMP_CD RECEMP; //←読み取り成功で、レコードロック!!
     IF %FOUND();
         W@MSSG = ' レコード読み取り中 :' + LOCK_ID;
         DSPLY W@MSSG;
     ELSE;
         W@MSSG = ' レコードが存在しません :' + LOCK_ID;
         DSPLY W@MSSG;
         LEAVESR;
     ENDIF;
      
   // ロック開放
      DSPLY '  ロックを開放しますか?  (Y/N)' '' UNLOCK_FLG;
      IF UNLOCK_FLG = 'Y';
          UNLOCK EMPLOYEES;
      ENDIF;
  ENDSR;
  //*************************************************************
  //* FINALIZE 
  //*************************************************************
  BEGSR #FINAL; 
      *INLR = *ON;
      RETURN; 
  ENDSR; 
 /END-FREE 

検証1 実行結果

従業員マスタレコード0001をUSER01のJOBがロックしている状態で、USER02がロックさせます。
すると、USER02では、初期値60秒を経過したのち、アベンド(プログラム異常終了)が発生してしまいました。

[USER01] CALL PGM(TIGEROBJ/SAMPLE39)   
[USER01] DSPLY   レコードIDを指定して下さい  ?
[USER01] ? 0001
[USER01] DSPLY   レコード読み取り中 :0001
[USER01] DSPLY    ロックを開放しますか?  (Y/N)   ※USER01は応答しないでおく。
[USER02] CALL PGM(TIGEROBJ/SAMPLE39)
[USER02] DSPLY   レコードIDを指定して下さい  ?
[USER02] ? 0001
[USER02] ロック Waiting -  60秒(デフォルト値)
[USER02] アベンド発生!「アプリケーション・エラー。 RNX1218 は, SAMPLE39 によって
         ステートメント 0000000044 命令 X'0000' で監視されていません。」
[USER02] プログラム異常終了
[USER01] ? Y     
[USER01] プログラム正常終了

レコードロックの確認方法

ロックが発生しているかどうかは、「DSPRCDLCKコマンド」で確認が可能です。
下記は、「DSPRCDLCK FILE(TIGERDB/EMPLOYEES)」を実行した結果になります。

                       メンバー・レコード・ロックの表示                         
                                                          システム :   AS400 
 ファイル . . . . . . :   EMPLOYEES       メンバー . . . . . . :   EMPLOYEES    
   ライブラリー . . . :     TIGERDB                                             
                                                                                
       レコード                                              ロック             
         番号      ジョブ      ユーザー    番号    状況      タイプ             
               1  USER01      USER01      701737   保留     UPDATE   ←ロックしているJOB      
                  USER02      USER02      701944   待機     UPDATE   ←ロック待機中のJOB

ロックウェイト時のWRKACTJOB

                               活動ジョブの処理 
                                                      
 オプションを入力して,実行キーを押してください。
   2= 変更   3= 保留     4= 終了   5= 処理   6= 解放   7=メッセージ の表示
   8=スプール・ファイル の処理   13= 切断 ...                              
                     現行                 
 OPT  サブシステム/ジョブ  ユーザー       タイプ  CPU %  機能             状況             
      QINTER         QSYS        SBS      .0                   DEQW   
        USER01       USER01      INT      .0  PGM-SAMPLE39     DSPW  
        USER02       USER02      INT      .0  PGM-SAMPLE39     LCKW  <<<<< Lock Waitの意味   

(3) ロックが解放される条件

ロックが解放される条件は、次の通りです。

(4) レコードロックをMONITOR文でキャッチする

レコードロックのついては、例外コード1218でキャッチすることが可能です。RPG言語では、MONITORで行います。
MONITOR命令については、以前のブログでまとめていました。

検証1で使ったソースに、例外処理を加えてみます。

f:id:no14141:20161121170349j:plain

検証2用ソースPGM RPGLESRC/SAMPLE39B

 BEGSR #MAIN;                                                
   MONITOR;                                                 
       DSPLY ' レコードIDを指定して下さい  ?' '' LOCK_ID; 
       EMP_CD = LOCK_ID;                                    
       CHAIN EMP_CD RECEMP;                                 
       IF %FOUND();                                         
           W@MSSG = ' レコード読み取り中 :' + LOCK_ID;      
           DSPLY W@MSSG;                                    
       ELSE;                                                
           W@MSSG = ' レコードが存在しません :' + LOCK_ID;  
           DSPLY W@MSSG;                                    
           LEAVESR;                                         
       ENDIF;                                               
   ON-ERROR 1218;                                           
       W@MSSG = ' レコードがロックされています :' + LOCK_ID;
       DSPLY W@MSSG;                                        
       LEAVESR;                                             
   ENDMON;                                                  
                                                            
 // ロック開放                                              
     DSPLY '  ロックを開放しますか?  (Y/N)' '' UNLOCK_FLG;     
     IF UNLOCK_FLG = 'Y';                                       
         UNLOCK EMPLOYEES;                                      
     ENDIF;                                                     
 ENDSR;                                                         
 //*************************************************************
 //* FINALIZE                                                   
 //*************************************************************
 BEGSR #FINAL;                                                  
     *INLR = *ON;                                               
     RETURN;                                                    
 ENDSR;                                                         
/END-FREE                                                       

検証2 実行結果

例外処理を組み込むことで、レコードロックを例外としてキャッチすることができます。
実施に実施してみると、アベンドではなく、ログを吐いて処理を継続してくれていることがわかります。

[USER01] CALL PGM(TIGEROBJ/SAMPLE39B)
[USER01] DSPLY   レコードIDを指定して下さい  ?
[USER01] ? 0001
[USER01] DSPLY   レコード読み取り中 :0001
[USER01] DSPLY    ロックを開放しますか?  (Y/N)   ※USER01は応答しないでおく。
[USER02] CALL PGM(TIGEROBJ/SAMPLE39B)  
[USER02] DSPLY   レコードIDを指定して下さい  ?
[USER02] ? 0001
[USER02] ロック Waiting -  60秒(デフォルト値)
[USER02] ログ出力「レコード 1 がジョブ 701737/USER01/USER01 で使用中。」
[USER02] ログ出力「ファイル EMPLOYEES のレコードを割り振ることができない。」
[USER02] DSPLY   レコードがロックされています :0001
[USER02] プログラム正常終了
[USER01] ? Y     
[USER01] プログラム正常終了
  • 例外をキャッチすれば、例外処理を記述する必要がありますが、正常終了になるため、上位からの呼び出しにも戻りコードをエラー扱いにするなどして、連携することが可能になります。

(5) デッドロックとは?

ロックといえば、「デッドロック」という概念が必ず出てきますね。
デッドロックとは、IT用語辞典を引用させていただきましょう。

デッドロックとは、複数のプロセスが互いに相手の占有している資源の解放を待ってしまい、処理が停止してしまうこと。データベースの排他制御の不備などが原因で起こる。(IT用語辞典

(6) RPGプログラムでデッドロックを発生させてみる

処理の流れは、下記の図のようになります。

20161121181628

検証PGMでは、パラメータを1で渡すと「従業員マスタ→部門マスタ」という順で呼び出してくれるのに対して、1以外の値をパラメータに渡すと、「部門マスタ→従業員マスタ」の順で呼び出されます。
すなわち、JOB別にそれぞれ別のテーブルのレコードをロックさせた状態で、次にすでにロックしているレコードを呼び合うことで、デッドロックが発生します。

検証3用ソースPGM RPGLESRC/SAMPLE39C

H DATEDIT(*YMD/) 
F****************************************************************
F* FILE 定義
F****************************************************************
F* 従業員マスタ
FEMPLOYEES UF   E           K DISK
F* 部門マスタ
FSECTIONS  UF   E           K DISK
F*
D****************************************************************
D* PROTOTYPE/INTERFACE 
D****************************************************************
D MAIN            PR                  EXTPGM('SAMPLE39C')
D                                1A   CONST
D MAIN            PI
DP@OPTION                        1A   CONST
D*
D****************************************************************
D* VARIABLE 定義
D****************************************************************
D EMP_LOCK_ID     S              4A   INZ(*BLANK)
D SEC_LOCK_ID     S              2A   INZ(*BLANK)
D UNLOCK_FLG      S              1A
D W@MSSG          S             50    INZ(*BLANK)
D*
 /FREE
  //*************************************************************
  //*  MAIN
  //*************************************************************
      IF P@OPTION = '1';
           EXSR  #SUB_EMP;
           EXSR  #SUB_SEC;
      ELSE;
           EXSR  #SUB_SEC;
           EXSR  #SUB_EMP;
      ENDIF;
      EXSR  #FINAL;
  
  //*************************************************************
  //*  従業員マスタ操作
  //*************************************************************
  BEGSR #SUB_EMP;
     DSPLY ' 従業員マスタIDを指定して下さい  ?' '' EMP_LOCK_ID;
     MONITOR;
         EMP_CD = EMP_LOCK_ID;
         CHAIN EMP_CD RECEMP;
         IF %FOUND();
             W@MSSG = ' 従業員レコード読み取り中 :' + EMP_LOCK_ID;
             DSPLY W@MSSG;
         ELSE;
             W@MSSG = ' 従業員レコードが存在しません :' + EMP_LOCK_ID;
             DSPLY W@MSSG;
             LEAVESR;
         ENDIF;
     ON-ERROR 1218;
         W@MSSG = ' 従業員レコードがロックされています :' + EMP_LOCK_ID;
         DSPLY W@MSSG;
         LEAVESR;
     ENDMON;
   // ロック開放
      DSPLY '  ロックを解除しますか?  (Y/N)' '' UNLOCK_FLG;
      IF UNLOCK_FLG = 'Y';
          UNLOCK EMPLOYEES;
      ENDIF;
  ENDSR;
  //*************************************************************
  //*  部門マスタ操作 
  //************************************************************* 
  BEGSR #SUB_SEC;
     DSPLY ' 部門マスタIDを指定して下さい  ?' '' SEC_LOCK_ID;
     MONITOR;
         SEC_CD = SEC_LOCK_ID;
         CHAIN SEC_CD RECSEC;
         IF %FOUND();
             W@MSSG = ' 部門レコード読み取り中 :' + SEC_LOCK_ID;
             DSPLY W@MSSG;
         ELSE;
             W@MSSG = ' 部門レコードが存在しません :' + SEC_LOCK_ID;
             DSPLY W@MSSG;
             LEAVESR;
         ENDIF;
     ON-ERROR 1218;
         W@MSSG = ' 部門レコードがロックされています :' + SEC_LOCK_ID;
         DSPLY W@MSSG;
         LEAVESR;
     ENDMON;

   // ロック開放
      DSPLY '  ロックを解除しますか?  (Y/N)' '' UNLOCK_FLG;
      IF UNLOCK_FLG = 'Y';
          UNLOCK SECTIONS;
      ENDIF;
  ENDSR;
  //*************************************************************
  //* FINALIZE
  //*************************************************************
  BEGSR #FINAL;
      *INLR = *ON;
      RETURN;
  ENDSR; 
 /END-FREE

検証3 実行結果

実際にデッドロックが発生させることができました。
先に例外をキャッチしたJOBが例外処理となり、後のJOBがレコードを読み取りできるようになります。 ということは、ロックをキャッチして、再度読み取りをトライ!などという処理をループ扱いにしてしまうと、永久に待ち合うことになってしまいます(おそろしや~)。

[USER01] CALL PGM(TIGEROBJ/SAMPLE39C) PARM('1') 
[USER01] DSPLY   従業員マスタIDを指定して下さい  ?
[USER01] ? 0001 
[USER01] DSPLY   従業員レコード読み取り中 :0001
[USER01] DSPLY    ロックを解除しますか?  (Y/N) 
[USER01] ? N 
[USER02] CALL PGM(TIGEROBJ/SAMPLE39C) PARM('2')
[USER02] DSPLY   部門マスタIDを指定して下さい  ? 
[USER02] ? 10
[USER02] DSPLY   部門レコード読み取り中 :01   
[USER02] DSPLY    ロックを解除しますか?  (Y/N) 
[USER02] ? N

[USER01] DSPLY   部門マスタIDを指定して下さい  ? 
[USER01] ? 10

[USER02] DSPLY   従業員マスタIDを指定して下さい  ?
[USER02] ? 0001 

<<デッドロック発生中!!>>
↓
※先に例外をキャッチしたJOBが例外処理となり、
  後のJOBがレコードを読み取りできるようになる。
(以下省略)

DSPRCDLCK

                       メンバー・レコード・ロックの表示                         
                                                          システム :   AS400
 ファイル . . . . . . :   EMPLOYEES       メンバー . . . . . . :   EMPLOYEES    
   ライブラリー . . . :     TIGERDB                                             
                                                                                
       レコード                                              ロック             
         番号      ジョブ      ユーザー    番号    状況      タイプ             
               1  USER01      USER01      701737   保留     UPDATE              
                  USER02      USER02      701944   待機     UPDATE              
                                                                          
-----------------------------------------------------------------------------------

                       メンバー・レコード・ロックの表示                         
                                                          システム :   AS400
 ファイル . . . . . . :   SECTIONS        メンバー . . . . . . :   SECTIONS     
   ライブラリー . . . :     TIGERDB                                             
                                                                                
       レコード                                              ロック             
         番号      ジョブ      ユーザー    番号    状況      タイプ             
               1  USER02      USER02      701944   保留     UPDATE              
                  USER01      USER01      701737   待機     UPDATE         

WRKACTJOB

                               活動ジョブの処理 
                                                      
 オプションを入力して,実行キーを押してください。
   2= 変更   3= 保留     4= 終了   5= 処理   6= 解放   7=メッセージ の表示
   8=スプール・ファイル の処理   13= 切断 ...                              
                     現行                 
 OPT  サブシステム/ジョブ  ユーザー       タイプ  CPU %  機能             状況             
      QINTER         QSYS        SBS      .0                   DEQW    
        USER01       USER01      INT      .0  PGM-SAMPLE39C    LCKW             
        USER02       USER02      INT      .0  PGM-SAMPLE39C    LCKW

デッドロックを発生させない定石として、トランザクション処理は、一定の順序でテーブルアクセスを行う」という点と、「読み取り成功するまでループ扱いにしない(例外処理を組み込む)」という2点でしょうか。

(7) 最大レコード待機時間の変更

DB2 for iでは、テーブル構築時に、最大レコード待機時間を指定できます。
デフォルト値が、60秒となっておりますので、よほどの事情が無ければこのままでいいように思います。

すでに運用中のテーブルの場合には、CHGPFコマンドで変更は可能です(取扱い注意! 私の環境では、CHGPF実行後にRPGのリコンパイルは不要でした)。
下記のコマンドでは、最大レコード待機時間を10秒に設定するケースです。

CHGPF FILE(TIGERDB/EMPLOYEES) WAITRCD(10)

(8) SQLでのロック制御

では、SQL文でのロックはどうするのか? ということで、DB2 for iでは「FOR UPDATE WITH 分離レベル」でロック処理ができました。

SELECT * FROM TIGERDB/EMPLOYEES 
 WHERE EMP_CD = '0001'  
   FOR UPDATE WITH RS    

分離レベルについては、データがアクセスされている間、 データが他のプロセスからどのようにロックされ分離されるかを決めるもので、複数指定ができます。きつめのロック、軽めのロックという具合でしょうか・・・。
こちらはJavaEEの場合、APサーバー側で設定し、EJBで自動トランザクション処理を行っているため、個別での操作は経験不足です。
いずれにしましても気をつけたい点は、COMMIT or ROLLBACKを実行するまで、ロックされ続けている点と、WHERE文によっては大量のレコードをロックしてしまう点でしょうか。

以上で、レコードロック検証についてのまとめ終了です。

【AS400】SQLRPGLE-挿入、更新、削除件数を取得する

久しぶりにAS400のSQLRPGLEネタです。

SQLのINSERT文、UPDATE文、DELETE文実行後に、どうやって結果レコード数を取得するのか? という疑問が湧きました。

Javaであれば、下記のようにSQL実行結果の戻り値で結果レコード件数を取得できるのですが・・・。

int updatedCount = stmt.executeUpdate(sql);

SQLCA(SQL Communications area)

SQLRPGLEでは、SQLCA(SQL Communications area)と呼ばれる領域の中から、更新レコード件数を取得できるようです。
SQLCAとは、プログラムとRDMSの間で、統計情報とエラー情報を共有するメモリ領域で、SQLRPGLEは変数定義せずに、SQLCAの変数を参照することができました。

具体的な変数は、SQLERRD配列の3要素目。

SQLにより更新、挿入、削除されたレコード数を「SQLERRD(3)」で取り出します。

その結果を戻り値などで返すパターンが実用的でありましょう。

サンプルSQLRPGLE

EXEC SQL                           
    DELETE TABLE_NAME                  
     WHERE COMPLETED = '1'            
       AND CRT_DATE <= :W@YYMD;      
                                   
CNT = %DEC(SQLERRD(3):7:0);