JNDIを使ったデータベース接続設定のXMLファイル定義

[[Eclipse/Tomcat] MavenのwebappプロジェクトでServlet+JSP](https://qiita.com/zaki-lknr/items/8137ac40ebd8f5bdb3c5) の続き。

参考情報

- [Apache Tomcat 8 (8.0.53) - JNDI Datasource HOW-TO](https://tomcat.apache.org/tomcat-8.0-doc/jndi-datasource-examples-howto.html#MySQL_DBCP_Example)
- [server.xmlを使用せずにJNDIでデータベースの接続設定を一元化する - 初めてのVPS構築](https://linux-svr.com/Java/32.php)

# 概要

元のソースだと、データベースサーバのアドレスなどの接続情報をJavaのソースコード内に記述していてメンテナンス性に欠ける。
そこでJNDIを使用して外部のXMLファイルに接続情報を定義するようにする。

前提として、「Mavenプロジェクトの`maven-archtype-webapp`」で作成したプロジェクト(おそらく「動的Webプロジェクト」含む)が対象。
「Tomcatプロジェクト」の場合は[後述](#tomcatプロジェクトの場合)

# サーバ情報の定義

## context.xmlファイルの作成

`WEB-INF`と同じ階層に`META-INF`ディレクトリを作成し、その直下に`context.xml`ファイルを作成する。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/3a1cf9e5-9d42-1814-1172-bc04c4cf6f7b.png)


[次へ]へテンプレートを選んだりできるみたいだけど、特に利用できるものがなさそう(見つけられなかった)ので、この画面で[完了]してファイル作成。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/3d06dcd9-5c9d-cb90-fa8d-0ef6466454ae.png)


## context.xmlにDB情報を記述

DBがMySQLの場合。
[Apache Tomcat 8 (8.0.53) - JNDI Datasource HOW-TO](https://tomcat.apache.org/tomcat-8.0-doc/jndi-datasource-examples-howto.html#MySQL_DBCP_Example) の"MySQL DBCP Example"の「2. Context configuration」参照

```xml:context.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context>

 <!-- maxTotal: Maximum number of database connections in pool. Make sure 
  you configure your mysqld max_connections large enough to handle all of your 
  db connections. Set to -1 for no limit. -->

 <!-- maxIdle: Maximum number of idle database connections to retain in pool. 
  Set to -1 for no limit. See also the DBCP documentation on this and the minEvictableIdleTimeMillis 
  configuration parameter. -->

 <!-- maxWaitMillis: Maximum time to wait for a database connection to become 
  available in ms, in this example 10 seconds. An Exception is thrown if this 
  timeout is exceeded. Set to -1 to wait indefinitely. -->

 <!-- username and password: MySQL username and password for database connections -->

 <!-- driverClassName: Class name for the old mm.mysql JDBC driver is org.gjt.mm.mysql.Driver 
  - we recommend using Connector/J though. Class name for the official MySQL 
  Connector/J driver is com.mysql.jdbc.Driver. -->

 <!-- url: The JDBC connection url for connecting to your MySQL database. -->

 <Resource
  name="jdbc/memoapp_db"
  auth="Container"
  type="javax.sql.DataSource"
  maxTotal="100"
  maxIdle="30"
  maxWaitMillis="10000"
  username="memoapp"
  password="memoapp"
  driverClassName="com.mysql.jdbc.Driver"
  url="jdbc:mysql://localhost:3306/memoapp_db" />

</Context>
```

Tomcatのサイトから記述内容を~~パクッ~~コピーして、`name`、`username`、`password`、`url`の部分を環境に合わせて変更する。
MySQL以外のDBを使用している場合は、DBに合わせた内容に変更する。

## web.xmlの設定

今度は`WEB-INF/web.xml`ファイルを編集する。
(Tomcatプロジェクトだとこのファイルは存在しないので作成する必要がある)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/4f7b8e2a-8aff-dbe1-2dfe-126769ce1c94.png)

```xml:web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app
 xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
 version="2.4"
>
 <description>memo app</description>
 <resource-ref>
  <description>DB Connection</description>
  <res-ref-name>jdbc/memoapp_db</res-ref-name>
  <res-type>javax.sql.DataSource</res-type>
  <res-auth>Container</res-auth>
 </resource-ref>
</web-app>
```

ただしTomcatのサイトに載っている上記の記述内容(version 2.4)と、EclipseのMavenプロジェクト(maven-archetype-webapp)が生成するデフォルトのweb.xml(version 2.3)はDTD宣言の有無など大きく違う。

とはいえ、2.3でもサンプルの`<resource-ref>`の部分を`<web-app>`に入れればOK


```xml:web.xml
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
 <display-name>Archetype Created Web Application</display-name>
 <servlet>
  <servlet-name>MemoAppMain</servlet-name>
  <display-name>MemoAppMain</display-name>
  <description></description>
  <servlet-class>jp.example.www.MemoAppMain</servlet-class>
 </servlet>
 <servlet-mapping>
  <servlet-name>MemoAppMain</servlet-name>
  <url-pattern>/</url-pattern>
 </servlet-mapping>

 <resource-ref>
  <description>DB Connection</description>
  <res-ref-name>jdbc/memoapp_db</res-ref-name>
  <res-type>javax.sql.DataSource</res-type>
  <res-auth>Container</res-auth>
 </resource-ref>
</web-app>
```

# MySQLへの接続コード

変更点は以下の通り

```diff
         try {
             Class.forName("com.mysql.cj.jdbc.Driver");
-            con = DriverManager.getConnection(url, user, pass);
+            Context initContext = new InitialContext();
+            Context envContext  = (Context)initContext.lookup("java:/comp/env");
+            DataSource ds = (DataSource)envContext.lookup("jdbc/memoapp_db");
+            con = ds.getConnection();
+            System.out.println("con: " + con);
             smt = con.createStatement();
```

接続情報を引数に`DriverManager.getConnection()`をコールしていたのを削除し、`Context#lookup()`でXMLに定義した`jdbc/memoapp_db`というリソースを探して接続する、という処理に変更する。

この変更によって、Javaコード内にMySQLのサーバ情報や認証情報を持たずに済むようになる。

# 備考

## web.xmlのバージョン

[web.xmlのバージョン別DTD・XSDの宣言方法 | KATSUMI KOKUZAWA'S BLOG](https://kokuzawa.github.io/blog/2015/04/07/web-dot-xmlfalsebaziyonbie-dtdxsdfalsexuan-yan-fang-fa/)

> JavaでWebアプリを作る場合、Mavenのmaven-archetype-webappテンプレートを利用しています。 非常に便利なのですが、Servlet 3.1が出ている今、 生成されたwe.xmlがServlet 2.3の記述になっていてちょっと古すぎます

ということで、各バージョンごとのweb.xmlの書き方が載っている。
Tomcat8であれば3.1は使用できるっぽいので、新しい書き方に書き直すのもアリ。

Eclipseの場合、web.xmlの変更以外に`<eclipse-project-path>/.settings/org.eclipse.wst.common.project.facet.core.xml`にもバージョン情報を持っており、手動で修正することで新規作成メニューでサーブレットを作成する際にもバージョンに合わせた動作になる。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/4bef34ca-5d0d-7ff7-01f2-3c345100746b.png)

本来はEclipseのプロジェクトプロパティのプロジェクト・ファセットで「動的Webモジュール」のバージョンを変更するものだと思われるが、なぜか画面から変更できないため、ファイルの内容の`<installed facet="jst.web" version="3.1"/>`の部分を直接編集する。(これが正しい対応かわからないけど…)

## Tomcatプロジェクトの場合

[Tomcatプロジェクト](https://qiita.com/zaki-lknr/items/32690b071abf202281d6)で作成したプロジェクトの場合、`META-INF/context.xml`を作成しても、EclipseからTomcatを起動した場合にプロジェクトの情報としてファイルを参照してくれない。
Tomcatプロジェクトの場合は、context.xmlに記述する`<Resource>`タグの内容をプロジェクトプロパティのTomcat設定の「その他の情報」に記述しておくと、実行時に読み込まれる。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/45cbdcbb-271f-c6e9-fcd2-10eb242a7965.png)

(もしくは、Tomcat自体の設定として、`<tomcat-dir>/conf/context.xml`も読み込まれる)