聞こえないJavaエンジニアが適当に書き連ねていく

つらつらとメモしたり日頃の溜まっている想いを吐き出す場所です。

無名SIerに勤めているただの人が関わったプロジェクトのドキュメント管理等について

これは何

オワコン大手SIerに学ぶアンチパターン - Qiitaを読んで、SIer勤務の身として 自分が関わっているプロジェクトはどんな感じなのか、ちょっと書いてみようと思ったものです。

昼休みに一気に書き上げたので足りていない部分はある。

バージョン管理

今のプロジェクトはSubversionを使っています。 Gitはちゃんと学ぶ時間を取らないと事故が起きてしまうので(実際に経験)避けています。

設計書編

画面設計書やバッチ設計書、帳票設計書等はお客様へのレビューの関係上、どうしてもExcelになってしまいます。 これは変えたいなとは思うものの、お客様とのやり取りを考えるとじゃあ何がいいのか、となったときに適切な代案が思い浮かびません。

Wordだと表が組みにくかったり、シートを分けて記述内容を変えたりということが出来ないのもExcelになってしまう要因です。

テーブル設計に関してはA5:SQL Mk-2 - フリーの汎用SQL開発ツール/ER図ツール .. 松原正和で生成できるER図を使っています。 いざとなったら、イメージ図にエクスポート出来る機能もあるので重宝しています。

社内用語に関しては、お客様が使っている用語をそのままというところがあるので慣れないと難しい、というのはその通りです。 用語集も最初はまとめていても、開発終盤になるとメンテナンスされなくなることもしばしば…。

設計編

画面設計に関しては、今のプロジェクトも含めてUIは余りかっこいいものになっていません。デザインをちゃんと学びたいとは思うのですが…。

画面の内部設計やバッチ設計については、一応それなりの設計になるように頑張ったので美しくはないけどスパゲッティでもないと思う。

DB編

テーブル名/カラム名がローマ字なのにはちゃんとした理由があって、作るシステムで使われている用語を英語にするのが難しいというのが第一理由です。 英語にしようと思えば出来なくもないのだけど、英語の候補にも複数出てきてどれを選択するのが適切なのかわからなかったり、スペルが難解だったりします。

そうなると、無理に英語にするよりローマ字にしたほうがまだパッと見て読めるのでローマ字を選択します。 ~日付とか~日時などの簡単なものは英語表記にするので、ローマ字と英語が混ざってしまうこともありますが、可読性を優先しています。

テーブルやカラム名の略称については、Oracleだとテーブル名やカラム名に30文字制限があってフルで入れるとどうしても削らないといけなくなり 短くしてしまうという背景もあると思われます。

テーブル名やカラム名に制限がないときは略称は使わないように心がけています。 とはいえ、丁寧に全部入れるととても長くなってしまうときは、一部分をカットすることもありますが…。

カラムの型については、適切なものを選択するようにしていますが、元システムのデータだったり、データ構成に関しては 日付項目なんだけどchar(8)を選択、等妥協してしまう場合もあります。 出来るだけ妥協しないように努めてはいますが…。

テーブルのカラム数は出来るだけ減らして、適切な単位で分割するように心がけています。

コード編

変数はテーブルのカラム名と合わせるルールにしたので、変な変数名はかなり減らせているはずです。 ハードコードも出来るだけ避けるようにコードレビューはしましたが、一回しか現れない値がどうしても残ってしまいました。

コードフォーマッタは環境構築の手順書の中に入れたので、マニュアルを読み飛ばさなければ保存時にある程度フォーマットしてくれるようにしました。 たまに設定漏れする人はいるので、見つけたら設定するように指導しています。

メソッドの行数は…業務ロジックもあるのでなかなか難しいところです。 今日の自分の分割方法と昨日の自分の分割方法にも違いが出てしまうくらい、自分の中でも明確なルールが定まっていないので自分もまだまだ勉強が必要です。

まとめ

  • 設計書がExcelの嵐になってしまうのは、お客様とのやり取りも考えるとどうしても避けられない部分はある。
  • 内部設計、テーブル設計、プログラミングに関しては中の人の問題なので、各自が経験を積んで適切なやり方を学ぶべし。
  • とはいえ、カリキュラムを組んで学ぶ時間がなくて、どうしても実務体験をベースになってしまう部分が多く、これはどうしたものかと常々思っている…。

LocalDateが範囲内かどうかをチェックする処理メモ

いつも調べてるのでメモっておく。

LocalDateクラスに標準で実装してくれればいいのにな。

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.time.LocalDate;
import java.util.Objects;
import org.junit.jupiter.api.Test;

class LocalDateUtilsTest2 {

    static boolean between(LocalDate target, LocalDate from, LocalDate to) {

        if (Objects.isNull(target)) {
            throw new IllegalArgumentException("targetの値が指定されていません。");
        }

        if (Objects.isNull(from)) {
            throw new IllegalArgumentException("fromの値が指定されていません。");
        }

        if (Objects.isNull(to)) {
            throw new IllegalArgumentException("toの値が指定されていません。");
        }

        if (target.isBefore(from)) {
            return false;
        }

        if (target.isAfter(to)) {
            return false;
        }

        return true;

    }

    @Test
    void target日付がnull() {

        try {

            between(null, LocalDate.now(), LocalDate.now());
            fail();
        } catch (IllegalArgumentException e) {

        }

    }

    @Test
    void from日付がnull() {

        try {

            between(LocalDate.now(), null, LocalDate.now());
            fail();
        } catch (IllegalArgumentException e) {

        }

    }

    @Test
    void to日付がnull() {

        try {

            between(LocalDate.now(), LocalDate.now(), null);
            fail();
        } catch (IllegalArgumentException e) {

        }

    }

    @Test
    void 日付比較() {

        assertTrue(between(LocalDate.now(), LocalDate.now(), LocalDate.now()));

        assertFalse(between(LocalDate.of(2019, 12, 31), LocalDate.of(2020, 1, 1),LocalDate.of(2020, 1, 3)));

        assertTrue(between(LocalDate.of(2020, 1, 1), LocalDate.of(2020, 1, 1),LocalDate.of(2020, 1, 3)));
        assertTrue(between(LocalDate.of(2020, 1, 2), LocalDate.of(2020, 1, 1),LocalDate.of(2020, 1, 3)));
        assertTrue(between(LocalDate.of(2020, 1, 3), LocalDate.of(2020, 1, 1),LocalDate.of(2020, 1, 3)));

        assertFalse(between(LocalDate.of(2020, 1, 4), LocalDate.of(2020, 1, 1),LocalDate.of(2020, 1, 3)));

    }


}

Spring Boot + Doma2のWebアプリでSQLをログファイルに出力したい

これは何

Spring Boot + Doma2のWebアプリをwarファイルにしてTomcatに配備する時、 SQLを独自のログファイルに出力したいのに出なかったので色々格闘したときのメモ。

各種バージョン等

Spring Bootのバージョン以外はあまり関係ないと思うが、一応。

  • Spring Boot 2.1.8
  • Doma2 2.26.0
  • Slf4j 1.7.28
  • Tomcat 9.0.17
  • Java 11

現象

Tomatのログとは別に、アプリケーションのログファイルに色々ログを出したいが、Doma2が出力するSQLが出力されない。

対応

どこかのプログラムに以下の2行を書く。 なんとなくだが、@Configuration アノテーションを付与したプログラムに書くのがいいかもしれない。

SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();

細かい考察等

org.springframework.boot.logging.Slf4JLoggingSystem.javaの中に以下の記述がある。

private void configureJdkLoggingBridgeHandler() {
    try {
        if (isBridgeJulIntoSlf4j()) {
            removeJdkLoggingBridgeHandler();
            SLF4JBridgeHandler.install();
        }
    }
    catch (Throwable ex) {
        // Ignore. No java.util.logging bridge is installed.
    }
}

/**
 * Return whether bridging JUL into SLF4J or not.
 * @return whether bridging JUL into SLF4J or not
 * @since 2.0.4
 */
protected final boolean isBridgeJulIntoSlf4j() {
    return isBridgeHandlerAvailable() && isJulUsingASingleConsoleHandlerAtMost();
}

protected final boolean isBridgeHandlerAvailable() {
    return ClassUtils.isPresent(BRIDGE_HANDLER, getClassLoader());
}

private boolean isJulUsingASingleConsoleHandlerAtMost() {
    Logger rootLogger = LogManager.getLogManager().getLogger("");
    Handler[] handlers = rootLogger.getHandlers();
    return handlers.length == 0 || (handlers.length == 1 && handlers[0] instanceof ConsoleHandler);
}

isBridgeHandlerAvailable() メソッドはslf4jライブラリが依存関係に含まれていれば常にtrueになりそう。

warファイルからの起動だと、isJulUsingASingleConsoleHandlerAtMost()falseになってしまうのではと推測はしたが…。

参考文献

上記、2行以外はほぼ以下の記述を参考にした&Spring Bootの基本的な記述通りにしている、つもりではいる。

Db2クライアントをインストールする手順メモ

Db2クライアントのインストール手順

AIX環境にDb2クライアントをインストールしたときの手順は以下の通り。

Db2クライアントのインストーラをダウンロード

  1. Download Fix Packs by version for IBM Data Server Client Packages にアクセスする
  2. Version11.1 のMod 4 Fix Pack 5 をクリックする。
  3. 遷移したサイトでは、IBM Data Server Runtime Clientの行にある、AIXのリンクをクリックする。
  4. 以降は画面に従って操作していくことで、インストーラをダウンロード出来る。

サーバーへのアップロード、解凍

  1. ダウンロードしたインストーラをそのままサーバーにアップロードする。
  2. tar.gz形式で圧縮されているので解凍する。

     gunzip -c v11.1.4fp5_aix64_rtcl.tar.gz | tar -xvf -
    

    rtclディレクトリが生成される。

インストーラを起動

実際にDb2クライアントを使いたいユーザーでログインして作業する。

  1. インストーラを解凍したディレクトリに移動する。

    今回は /work/rtcl ディレクトリに解凍したので、cd /work/rtcl で移動する。

  2. db2_install -L JP を実行する。途中で数回、聞かれるが、全てyesを入力でよい。

    • -L JPオプションをつけないと、db2コマンドを実行したときのメッセージが英語になってしまう。
    • db2setupGUIで作業するインストーラのようで、ターミナルエミュレータから実行しようとするとエラーになる。
      • 応答ファイルを使ったインストールの方法もあるようだが、記載方法がわからなかったので保留。
    • インストール作業中に容量不足と表示されたときは、/homeディレクトリの容量を拡張する。

動作確認

インストール作業が終わったら、一度ログアウトしてから再ログインする。 再ログインしたら、db2 ?コマンドを実行して、ヘルプが表示されればインストールに成功している。

Java8以降で指定した日付の前、または次の曜日の日付を求める

忘れそうなのでメモです。

TemporalAdjusters.java を使う。

docs.oracle.com

@Test
void test() {

    assertEquals(LocalDate.of(2020, 3, 24),
            LocalDate.of(2020, 3, 31).with(TemporalAdjusters.previous(DayOfWeek.TUESDAY)));

    assertEquals(LocalDate.of(2020, 4, 7),
            LocalDate.of(2020, 3, 31).with(TemporalAdjusters.next(DayOfWeek.TUESDAY)));

}

AIX 7.2にApacheをインストールする手順メモ

AIX 7.2にApacheをインストールする手順メモ

AIXにインストール済となっていたapacheがインストールミスで起動できなかったのでイチからやり直したときの手順メモ。

調べたところ、rpmでインストールする必要がある模様で、rpmAIX Toolboxからダウンロード出来るとのこと。

前提

  • サーバーは直でインターネットに繋がらないネットワーク上にある。
  • Tomcatは既にインストール済(バージョンは9.0.17)。
  • rootユーザーで作業する。

必要なrpmをそろえてインストールする

AIX Toolbox for Linux Applications - Downloads alphaからダウンロードする。

  1. Apache Httpサーバーをインストールしたかったので、まずはhttpdを探して、Binary RPMのリンクを押す。これでrpmファイルがダウンロードされる。
  2. FTPツールを使って、ダウンロードしたhttpd-2.4.41-1.aix6.1.ppc.rpmをサーバーにアップロードする。
  3. ターミナルエミュレータでサーバーにログインし、以下のコマンドを実行してインストールテストを行い、必要なパッケージを確認する。

     rpm -i --test httpd-2.4.41-1.aix6.1.ppc.rpm
    
  4. 表示されたパッケージ、aprapr-utilopenldapAIX Toolboxからダウンロードする。

  5. ダウンロードしたパッケージ、それぞれに対してインストールテストを行い、足りていないパッケージを確認してダウンロードする、を繰り返す。
  6. 最終的に必要と表示されたパッケージは以下の通り(ファイル名順)。

  7. インストールテストを行った結果、以下の順番でインストールを行う。

    1. apr-1.5.2-1.aix6.1.ppc.rpm
    2. cyrus-sasl-2.1.26-3.aix6.1.ppc.rpm
    3. readline-8.0-2.aix6.1.ppc.rpm
    4. openldap-2.4.48-1.aix6.1.ppc.rpm
    5. sqlite-3.28.0-1.aix6.1.ppc.rpm
    6. apr-util-1.5.4-1.aix6.1.ppc.rpm
    7. httpd-2.4.41-1.aix6.1.ppc.rpm

    コマンドは rpm -ivh パッケージファイル名。 既に古いバージョンがインストールされていると出たときは更新するのでrpm -Uvh パッケージファイル名

    今回の作業時は以下のコマンドを実行した。

     rpm -ivh apr-1.5.2-1.aix6.1.ppc.rpm
     rpm -ivh cyrus-sasl-2.1.26-3.aix6.1.ppc.rpm
     rpm -Uvh readline-8.0-2.aix6.1.ppc.rpm
     rpm -ivh openldap-2.4.48-1.aix6.1.ppc.rpm
     rpm -Uvh sqlite-3.28.0-1.aix6.1.ppc.rpm
     rpm -ivh apr-util-1.5.4-1.aix6.1.ppc.rpm
     rpm -ivh httpd-2.4.41-1.aix6.1.ppc.rpm
    

    もし、グループやユーザーが足りていなかったときはインストーラが自動で追加してくれる。 念のため、smitコマンドを使って追加されたグループやユーザーの設定を確認しておく。

  8. インストールされたかどうかの確認をする。

     lslpp -Lc | grep httpd
    

    インストールが成功していれば表示される。

     httpd:httpd-2.4.41-1:2.4.41-1: : :C:R:Apache HTTP Server: :/bin/rpm -e httpd: : : : :0: :/opt:Thu Sep 26 14:34:54 JST 2019
    

以上で初期インストールは完了。

起動、停止確認

apacheが正しく起動するかどうかを確認する。 起動用のコマンドはrpm -ivh httpd-2.4.41-1.aix6.1.ppc.rpmが成功したときにコンソールに表示されている。

Updating / installing...
1:httpd-2.4.41-1                   ################################# [100%]
This version of httpd has 32bit and 64bit support
To start 64bit httpd use : /opt/freeware/sbin/apachectl_64 start ★これが起動用のコマンド

/opt/freeware/sbin/apachectl_64 startコマンドを実行し、表示されるIPアドレスをブラウザに打ち込んでIt works!が表示されたらapacheが起動している。

停止できることも確認しておく。

/opt/freeware/sbin/apachectl_64 stop

再起動コマンドは以下。

/opt/freeware/sbin/apachectl_64 restart

起動しているかどうかをプロセスで確認するには、以下のコマンドを実行する。

ps -ef | grep httpd

confファイルの場所確認

細かいapacheの設定はconfファイルを使って行うので場所を確認しておく。 rpm -qlp httpd-2.4.41-1.aix6.1.ppc.rpmコマンドを実行すると、配備されるファイル一覧が表示されるので、一覧からhttpd.confを探して確認する。 今回は /opt/freeware/etc/httpd/conf/にconfファイルが置かれていた。

Tomcatと連携する

本来の目的は、apacheTomcatを連携させたいことだった。 ポート番号無しでアクセスしたとき、Tomcatに繋がるようにするにはajpを使う。

Tomcat側の設定

Tomcatがインストールされているディレクトリ/conf ディレクトリの下にserver.xmlが置かれているので、このファイルを編集する。

以下の記述を探し、コメントアウトされていたらコメントを外して有効にする。

<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" useBodyEncodingForURI="true" />

ポート8080での接続を拒否する場合は、<Connector port="8080" protocol="HTTP/1.1"で始まる部分を削除するかコメントアウトする。

server.xmlを編集したら、Tomcatを再起動する。

Tomcatがインストールされているディレクトリ/bin/shutdown.sh
Tomcatがインストールされているディレクトリ/bin/startup.sh

以上でTomcat側は完了。

apache側の設定

まず、ajp接続に必要なモジュールを読み込むように httpd.confまたはhttpd.conf_64を修正する。 OSが64ビット版なら、httpd.conf_64を編集する。

confファイルを検索し、mod_proxy.somod_proxy_ajp.soが含まれる行を探す。 どちらも初期設定だとコメントになっているので、先頭の#を消して有効にする。

次に、ポート番号無しで特定のURLでアクセスがあった時にTomcatに接続する設定を追加する。 直でconfファイルに書いてもよいが、別ファイルに定義を書いてそのファイルを読み込む形が主流のようなので、それに従う。

Include conf/extra/が書かれている部分を検索し、その部分に行を追加する。

# Tomcatとの連携
Include conf/extra/httpd-proxy-ajp.conf

次に、httpd-proxy-ajp.confファイルを新規で作成し、以下の記述を追記する。

<Location /Webアプリケーションのコンテキストルート/>
ProxyPass ajp://localhost:8009/Webアプリケーションのコンテキストルート/
</Location>

例えば、コンテキストルートがdocsの時は以下の記述になる。

<Location /docs/>
ProxyPass ajp://127.0.0.1:8009/docs/
</Location>

対象にしたいコンテキストルートが複数あるときは、<Location>タグを並べて書けばよい。

<Location /docs1/>
ProxyPass ajp://127.0.0.1:8009/docs1/
</Location>
<Location /docs2/>
ProxyPass ajp://127.0.0.1:8009/docs2/
</Location>

confファイルを編集したら、所定のディレクトリに配備する。

作業したときの配置場所は以下の通り。

/opt/freeware/etc/httpd/conf/httpd.conf_64
/opt/freeware/etc/httpd/conf/extra/httpd-proxy-ajp.conf

最後にapacheを再起動して、ポート番号無しで繋がることを確認する。

/opt/freeware/sbin/apachectl_64 restart

参考にしたサイト

JShellを使ってDBサーバーにjdbc接続が出来るかどうかを確認する

これは何

APサーバーからDBサーバーにjdbc接続が出来るかどうかを確認する手順メモです。

前提

  • JShellを使うのでJDK9JDK11以上がインストールされている必要があります。
  • DBサーバーに応じたドライバのJarファイルを別途入手する必要があります。

書いた人はDb2で確認したので、以後の記述はDb2での確認手順です。

手順

jshellを起動

クラスパスにドライバのJarファイルを追加してjshellを起動します。

jshell --class-path jcc-11.1.4.4.jar

jshell内で接続確認

var conn = java.sql.DriverManager.getConnection("jdbc:db2://localhost:50000/TESTDB", "db2admin", "db2admin");

DriverManager (Java SE 11 & JDK 11 ))

接続できないときはエラーが発生します。

念のためURLが正しいことを確認します。

conn.getMetaData().getURL();

確認出来たらコネクションは閉じましょう。

conn.close();

jshellを終了

/exit

まとめ

プログラムを書かなくてもjshellで疎通確認ができるのは楽でいいですね。