ゆるくエンジニア日記

職業はシステムエンジニア。日々の学びや感じたことをゆるく記録していきます。

GoogleMapAPI JavaScriptコンソールで技術情報を...

困ったこと

Google Map APIを使って開発をしていたところ、家では正常にGoogle Mapが表示できるのに、外で表示しようとしたらこのようなエラーがでた。
JavaScriptコンソールで技術情報を見たいけど、、、スマホで開発者ツールとか開けるんだっけ?

やったこと

こちらを参考にして、スマホにKiwi Browserをインストール

kurima.hatenablog.com

Kiwi Browserでエラーとなっている該当のページを開き、開発者ツールでコンソールログを見てみる。

原因

RefererNotAllowedMapErrorとでていた。
そ、そういえばGoogle Map APIの設定画面でそのような項目があったような。。。
原因は、自宅WifiのみのIPアドレス範囲に限定していたことでした。

対応

IPアドレスでの制限ではなく、HTTPリファラーでの設定にしました。
今回の件でKiwi Browserを知ったけど、PCの時と同じ感覚で開発者ツールをみることができてよかった。
デバッグとかその他諸々の機能は全然使っていないけど)

ymlファイルを読み込みでハマったこと

事象

  • application.ymlファイルを、秘匿したい情報と公開してもよい情報で分けて管理しようと考えた。
  • application.ymlファイルと、application-security.ymlファイルで分けて作成した。
  • application.ymlファイル内に、spring:profiles:active:securityを追加した。

→ 全く読み込まれない。

試したこと

  • application.ymlファイルに戻せば、正常に動作する。
  • 別に作っておいた、application-local.yml等のファイルに記載すれば、正常に動作する。

原因

  • 改行コード原因のようだった。
  • エラーでずっと読み込まれなかったのは、CRLF
  • 成功していたのは、LFのみ

まとめ

文字コード系や改行コード等、見た目ではわからなくて普段意識しない領域に自分はハマりやすい傾向がある。。。

エラー備忘録 NoClassDefFoundError

個人的に別のツールにまとめている除法を整理している中で、少し印象に残っていたエラーがあったので、ご紹介。
ただ、あまり詳しく書きすぎるとあれなので、少し抽象化した形で記載します。

NoClassDefFoundErrorとは

以下公式ドキュメントより

通常のメソッド呼び出し、あるいはnew式を使った新しいインスタンスの生成で、Java仮想マシンまたはClassLoaderインスタンスがクラス定義をロードしようとしたが、クラス定義が見からない場合にスローされます。 検索されるクラス定義は、現在実行中のクラスをコンパイルする時点では存在していましたが、その後見つからなくなっています。
NoClassDefFoundError (Java SE 17 & JDK 17)

直接的な原因は別の UnsatisfiedLinkError

当時対応していた時は、ひたすらNoClassDefFoundErrorに関する情報を収集していたが、実は潜在的な問題が別にあり、このエラーは直接的なエラーではないことがわかった。
直接的なエラーは、UnsatisfiedLinkErrorというもの。
このエラーがでてしまった場合、JVMがそのエラーがでたクラスを記憶し、再度そのクラスにアクセスしようとすると、NoClassDefFoundErrorがでるという事象だった。
そのため、このエラーは初回起動してから初回クラスアクセス時にはコンソールに出力されるが、その後は何回処理を実行しても、でてこなくなる。

UnsatisfiedLinkErrorとは

Java仮想マシンが、nativeと宣言されたメソッドの適切なネイティブ言語の定義を見つけることができない場合にスローされます。
UnsatisfiedLinkError (Java SE 17 & JDK 17)

結局のところの原因

結局のところは、必要なDLLなどのモジュールが配置されている箇所へのパスが通っていないことが原因でした。
わかった時は、そんな凡ミスか、、、と嘆いたのはいい思い出ですが、、、
長く時間を費やしても解決できないようなエラーって、経験上こういう凡ミスやそもそもの前提の認識が誤っていたとか往々にしてあると思います。

ついでに ClassNotFoundException との違いは?

当時調査している中で調べてみたので、ついでに。
大きな違いは、ClassNotFoundExceptionは、コンパイルすらできるかわからない状況であるということ。
NoClassDefFoundErrorの場合は、公式にも記載されている通り、コンパイル時点では存在していたはずのクラスということ。
もう少し具体的になると、ClassNotFoundExceptionは以下のパターンで発生する。

補足

ここで記載しているエラーは、あくまで一例なので、NoClassDefFoundErrorUnsatisfiedLinkErrorが発生する原因は、他の場合も多々あります。
特にNoClassDefFoundErrorなんかは、ClassLoaderの読み込み優先順位や参照権限の問題なども取り上げられており、頭が混乱していました。
もし同じようなことでお困りの方の助けになれば。。。

JavaScriptで2次元配列を宣言する方法

JavaScriptで単一の配列は作成することはあったが、2次元配列は意外と少なかった。
今回2次元配列を使おうと思った時に、意外と詰まってしまったので備忘録。

NG例①

最初は以下のように記載していた。

var test = [];
for(let i = 0; i < icnt; i++){
  for(let j = 0; j < jcnt; j++){
    test[i][j] = "hogehoge";
  }
}

ダメだった。どうやらtest[i]に対して、j番目の配列というのがundefinedとして認識されているらしい。
ということは2次元配列として宣言してあげればいけるのか?
と思い試してみたのが以下。

NG例②

var test = [][];
for(let i = 0; i < icnt; i++){
  for(let j = 0; j < jcnt; j++){
    test[i][j] = "hogehoge";
  }
}

今度は構文エラーになる。だめらしい。

調べてみると、以下のようにする必要があるらしい。

OK例

var test = [];
for(let i = 0; i < icnt; i++){
  for(let j = 0; j < jcnt; j++){
    test[i]=[];
    test[i][j] = "hogehoge";
  }
}

なんとなく冗長がするのと、宣言時に2次元配列であることがわからないので若干もやもやするが、、、
でもこれはおそらくJavaに慣れすぎているせいで、JavaScriptに慣れていれば、動的型付けゆえの仕様ということで納得できるか?

ちなみに、宣言部分はnew Arrayでも置き換えできる。

参考リンク

JavaScriptで多次元配列を扱う | プロカツ!

docker runとdocker-composeでのportの違い

コンテナ技術の勉強として、DockerでアプリとDBをつないでみた。
その時にポートの設定で少し詰まったところと、検証してみてわかったことがあったので、備忘録。

イメージ

それぞれの違い

docker runの場合
docker runで立ち上げた場合、コンテナ間の通信には、外部アクセス用のポートが使用される。

docker-composeの場合
docker-compose upで立ち上げた場合は、コンテナ間の通信はその内部のポートが使用される。

docker run で構築する場合の手順

※アプリ・DBそれぞれ単体で立ち上げて動作することは確認済みの前提。

1. ネットワークを作成する

アプリとDBをつなぐためのネットワークを作成する。

docker network create hoge_network

2. アプリ側のコンテナを作成する

※ここでは、Tomcatに対してSpringBootで作成したアプリケーションを構築したものを想定する。

2-1. アプリケーション定義ファイルの作成

作成するアプリ側の定義ファイルは、以下の通り。

spring:
  datasource:
    url: jdbc:postgresql://db:5433/hogedb
    username: postgres
    password: postgres
    driver-class-name: org.postgresql.Driver
  jpa:
    database-platform: org.hibernate.dialect.PostgreSQLDialect
    show-sql: true
    hibernate:
      ddl-auto: none

ここで、urlに指定するホスト名には、docker runする時に指定するネットワークのエイリアス名。ポート番号は、外部から接続するためのポート番号を指定する。

2-2. Dockerfileの作成

イメージを作成するために、Dockerfileを作成する。

# Dockerfile
FROM tomcat:9.0.64
ENV CATALINA_HOME /usr/local/tomcat
ENV PATH $CATALINA_HOME/bin:$PATH
ENV WAR_FILE target/*.war
WORKDIR $CATALINA_HOME
COPY ${WAR_FILE} webapps/hoge.war
2-3. イメージの作成

ビルド用のコマンドを実行する。

docker build -t hoge_user/hoge .
2-4. コンテナの立ち上げ

イメージができたら、docker runを実行する。

docker run -dp 8888:8080 --name hoge_container --network hoge_network --network-alias app hoge_user/hoge

少し日本語訳すると、

  • 先ほどビルドして作成した[ hoge_user/hoge ]というイメージを使って、コンテナを作成・起動する。
  • コンテナの名前は、[ hoge_container ]として作成する。
  • コンテナ外からのアクセスは8888ポートで受け付け、コンテナ内の8080ポートとしてポートフォワードする。
  • ネットワークは、[ hoge_network ]に入り、ホスト名を[ app ]とする。

3. DB側のコンテナを作成する

※DB側コンテナのベースとなるイメージはpullしている前提。

3-1. DB側のコンテナの立ち上げ

postgres:14.0のイメージを使用して、コンテナを作成・起動する。

docker run -it -d `
--network hoge_network --network-alias db `
--name hoge_postgres `
-e POSTGRES_PASSWORD=postgres `
-e POSTGRES_DB=hogedb `
-e POSTGRES_USER=postgres `
-p 5433:5432 `
postgres:14.0

アプリ側と同じように、コンテナの名前とポートフォワーディング、ホスト名の設定を行う。

docker-compose で構築する場合の手順

この場合は、docker-compose.ymlファイルさえ作成すれば準備は完了する。

1. docker-compose.ymlの作成

version: '3.6'
services:
  app:
    container_name: hoge_container
    image: hoge_name/hoge
    ports:
      - 8888:8080
    depends_on:
      - db
  db:
    container_name: hoge_postgres
    image: postgres:14.0
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: hogedb
    ports:
      - 5433:5432
  

2. コンテナの立ち上げ

下記のコマンドを実行する。-fオプションで指定しているのは、1で作成したdocker-compose.ymlのファイルパス。

docker-compose -f docker-compose.yml up -d

まとめ

今の自分にはdocker-composeを使った方が楽に感じる。
でもたぶんどこかでdocker runで構築しないといけないような場面もでてくる気がする。

【Java】Struts + EJBについて整理してみる - 2

本記事の目的

  • StrutsEJBJ2EEの関係について、イメージできるようにする。 ※開発現場によって多少の違いがあると思いますので、そこはご容赦ください。

本題

全体像

開発の流れ
  1. war用のプロジェクトと、EJBjar用のプロジェクトで開発
  2. build.xmlでearファイルを作成するように定義
  3. ビルド
  4. 動作させたい環境にデプロイ
    ※ どこかのタイミングで、デプロイ用の定義ファイルを作成する必要があります。
処理の流れ
  1. クライアントからリクエストを発行する
  2. WEBサーバーがリクエストを受け付け、WEBコンテナへリダイレクトする
  3. WEBコンテナ側での処理を実行
  4. 必要があれば JNDI を利用して、EJBコンテナ側と連携する
  5. EJBコンテナ側で処理を実行
  6. 必要であれば JNDI を利用して、DBへのアクセスを実行
  7. 各処理結果をまとめて、JSPコンパイル処理を実行
    (※設定によっては事前にコンパイルされているケースもある)
  8. レスポンスを返却する
Strutsの動き

※整理していて、自分自身も詳しく理解できていないところがあると気づいたので、また整理できたら追記していこうと思います。

  1. ActionServletがリクエストを受け付ける
  2. web.xmlstruts-config.xmlの定義に基づいて、URIからActionFormを生成し、Actionのexecuteメソッドを実行する。
  3. 必要があれば、Action内でEJBの処理を呼び出す
    • JNDIのlookup経由でHomeインターフェースを呼び出し、createメソッドを実行し、Remoteインターフェースを取得
    • Remoteインターフェースから、任意のメソッドを実行
  4. 処理結果をまとめて、ActionForwardを返す。(Actionクラスの戻り値)
  5. JSPコンパイルを実施し、HTMLにしてレスポンスを返却
EJB(とiBatis)の動き

  1. ActionからJNDI経由でBeanクラスを生成(ステートレスセッションBeanの場合は、プーリング領域から取得)
  2. 要求された処理を実行
  3. 必要があれば、DaoManagerを利用してデータソースへのアクセスを実行し、任意のクエリを発行する
  4. 処理結果を返す

ここまでの補足事項としては、EJBについては、ローカルBean用の仕組みを使えば、JNDI経由で呼び出さずとも、クライアントのサーバー内で解決ができるようです。(初めて知った。。。)
また、自分でも結構曖昧だと感じた部分が多かったので、誤りもあるかもしれません。

次から整理する内容は、より実装ベースの話に入ろうかとも思ったのですが、調べると結構実装例はたくさんあるので、やめておきます。(環境構築がめんd...)

ただ、最近勉強しているコンテナを使って、J2EEベースのStruts+EJB+iBatisのイメージを作ってみるのは、面白そうかな?と思いました。

気づき
  • Strutsの詳しい処理について、よくわかっていない
  • EJBの呼び出し周りについて、よくわかっていない
  • DAOの詳しい処理について、よくわかっていない
    → ある意味、わかっていなくても色々連携ができるという意味では、フレームワークとしての役割を果たしてもらっている。
    (ちゃんと時間があるときに調べてみます。)
参考リンク
J2EEEJB関連

atmarkit.itmedia.co.jp

www.ulsystems.co.jp

atmarkit.itmedia.co.jp

EJBの参考

www.samuraiz.co.jp

iBatisの参考

codezine.jp

【Java】Struts + EJBについて整理してみる

背景

WEBエンジニアとして働き始め早4年。
現場で扱っている技術について、棚卸もかねて整理しようと思い立ちました。

目次

Strutsとは?

概要
ポイント
  • MVCモデル
  • Servlet / JSP
  • 様々なタグライブラリ
  • 下記のクラスやファイル達
    • ActionServlet
    • struts-config.xml
    • validator-rules.xml
    • Acion
    • ActionForm

Strutsを触り始めた当初、下記サイトには大変お世話になりました。 www.javaroad.jp

EJBとは

概要
  • Enterprise Java Beansの略であり、業務システムを意識した、コンテナ/コンポーネント技術。
  • J2EEと密接に関係している(むしろJ2EEがないとできない)
  • 周りの環境に左右されず、ビジネスロジックに専念できるようにするために考えられた。← !!重要!!
ポイント
  • J2EE
  • 分散オブジェクト技術
  • CORBA
  • JNDI
  • RMI

一旦ここまで。
補足事項としては、初期のStrutsEJBの仕様は、XMLベースでの設定であったため、保守のしにくさや複雑さ等がありました。
そこから改良を重ねて、アノテーションベースや、契約による設計を取り入れてきたようです。(自分は触ったことないですが)

気づき
  • プログラミングする領域を、MVCモデルとして境界を意識させることで、(できるだけ) 神クラスが生まれないようにしようとした。
  • ドメイン領域(ビジネスロジックの領域)については、クライアントやインフラ等の環境に左右されないように設計するという理念については、今も昔も変わっていない。
  • アノテーションベースや契約による設計の目的は、他ファイルで設定値を定義することによる保守性の低さを解決するため。
    (= アノテーションベースや契約による設計でむしろ保守性が下がる場合は、別の手段も検討すべき?そんな場面は滅多にないと思うけど、、、)

次は、StrutsEJBJ2EEの関係を、もう少し具体的なイメージに落とし込んでみたいと思います。

用語説明
  • MVCモデル:サーバー側の処理を、Model, View, Controllerの3つの役割に分けることで、プログラミングで意識する領域を限定させる、アプリケーション設計の1つ。
  • 神クラス:1つのクラスに色んな機能が詰め込まれたクラス。代表的なものは、Utilクラス。「何か特定領域のUtilクラス」は個人的に許容できる範囲だと思うが、「何でも屋Utilクラス」が存在すると、神クラス化しやすい。