[Oracle Cloud] Apache Tomcat から ATP に接続する on CentOS

# はじめに

Oracle Cloud Infrastructure(以下OCI)では、フルマネージドな自律型データベースサービスのAutonomous DBを提供しています。
パッチ適用、DB監視、バックアップ、チューニング、障害解決など、DB管理の全ライフサクルを自動化するため、保守運用作業を軽減することが可能です。

Autonomous DB は、現時点では以下の2種類のサービスを提供しています

- Autonomous Transaction Processing(ATP) : 汎用的なオンライントランザクション用途
- Autonomous Data Warehouse(ADW) : データウェアハウス用途



今回の記事では、CenetOS7, Apache Tomcat9, Apache HTTP Server から、ATPへ接続する方法を記載します。



# 環境構築編

## ATP作成

OCI上で、Autonomous Transaction Processing(ATP)を適当に作成します。
必要に応じて、Documentを参照します。
https://docs.cloud.oracle.com/iaas/Content/Database/Tasks/adbcreating.htm

以下のパラメータでCreateを実行。Create完了まで数分かかります。

- Display name : atpdisname
- Database name : atpdataname
- Choose a workload type : Transaction Processing
- Chose a deployment type : Serverless
- CPU core count : 1
- Storage(TB) : 1
- Auto scaling : on
- Create administrator credentials password : 適当に (今回の例では、passwordPassword1で作成) 
- Choose a license type : License Included



## OCI の仮想ネットワークのポート開放

仮想インスタンスを作成する仮想ネットワーク上で、Security List と、Network Security Group のポートを開放します。

- TCP 80 (Apache HTTP Server)
- TCP 8080 (Apache Tomcat)



## インスタンスの構成

### インスタンス作成, Oracle JDK Install

OCI 上で Cent OS を建てて、Oracle JDK をインストールします。以下の記事を参照
https://qiita.com/sugimount/items/33c65e2f648ea46c7239



### Firewalld 停止

OCIの仮想ネットワークの機能で、以下のFirewall の機能があるため、インスタンス上の Firewalld を停止します

- Security List (サブネット単位の設定)
- Network Security Group (NIC単位の設定)

```
systemctl stop firewalld
systemctl disable firewalld
```



### Apache Tomcat を Install

yum を使えば、Repositoryからtomcatをinstallすることが出来ますが、最新版を使用したいため Apache Tomcat の最新版(9.0.22) から install を行います

まず、ユーザーを追加します

```
useradd -s /sbin/nologin tomcat
```



Tomcatの9.0.22 を Install するための tar.gz をダウンロードします。

```
cd ~
wget http://ftp.kddilabs.jp/infosystems/apache/tomcat/tomcat-9/v9.0.22/bin/apache-tomcat-9.0.22.tar.gz
```



tar.gz を解凍して、/opt 配下に格納します

```
tar -xzvf ~/apache-tomcat-9.0.22.tar.gz
mv ~/apache-tomcat-9.0.22 /opt
chown -R tomcat:tomcat /opt/apache-tomcat-9.0.22
```



systemd で管理するために、ファイルを作成します

```
cat <<'EOF' > /etc/systemd/system/tomcat.service
[Unit]
Description=Apache Tomcat 9
After=network.target

[Service]
User=tomcat
Group=tomcat
Type=oneshot
PIDFile=/opt/apache-tomcat-9.0.22/tomcat.pid
RemainAfterExit=yes

ExecStart=/opt/apache-tomcat-9.0.22/bin/startup.sh
ExecStop=/opt/apache-tomcat-9.0.22/bin/shutdown.sh
ExecReStart=/opt/apache-tomcat-9.0.22/bin/shutdown.sh;/opt/apache-tomcat-9.0.22/bin/startup.sh

[Install]
WantedBy=multi-user.target
EOF
chmod 755 /etc/systemd/system/tomcat.service
systemctl daemon-reload
```



tomcat を起動します。

```
systemctl restart tomcat
systemctl enable tomcat
```



仮想インスタンスの PublicIPの8080番ポートにアクセスして、Tomcat Logo が表示されることを確認します

![1563681679573.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104247/1ecc5aff-62ad-b6ae-5595-1a07311ea009.png)



### Apache HTTP Server を Install

Apache HTTP Server をインストールします

```
yum install -y httpd
```



依存関係解決memo

```
===========================================================================================================================================================
Package                             Arch                           Version                                       Repository                          Size 
===========================================================================================================================================================
Installing:
 httpd                               x86_64                         2.4.6-89.0.1.el7_6                            ol7_latest                         1.2 M 
Installing for dependencies:
 apr                                 x86_64                         1.4.8-3.el7_4.1                               ol7_latest                         103 k 
 apr-util                            x86_64                         1.5.2-6.0.1.el7                               ol7_latest                          91 k 
 httpd-tools                         x86_64                         2.4.6-89.0.1.el7_6                            ol7_latest                          90 k 
 mailcap                             noarch                         2.1.41-2.el7                                  ol7_latest                          30 k 

Transaction Summary
===========================================================================================================================================================
```



起動

```
systemctl restart httpd
systemctl enable httpd
```





仮想インスタンスに紐づいている Public IPの80番ポートにアクセスすると、以下のページが表示されます

![1563379863890.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104247/ffc81906-bbe6-1941-aea1-faeaf67b9438.png)



### Apache HTTP Server と Tomcat の連携

Apache HTTP Server と Tomcat 間の通信は、AJP(Apache JServ Protocol) という名前のプロトコルが使用されています。
Apache HTTP ServerからTomcat宛に ASP のリクエストを送付すると、リクエスト情報に従って、Tomcat は Servlet を起動します。

Tomcatの設定ファイルを確認します。

```
less /etc/tomcat/server.xml
```



以下のように、8080ポートと、8009ポートを受け付ける設定となっています。8080ポートは、TomcatとしてHTTPのアクセスを受け付けるポートとなっており、8009はServletを起動するためのポートとなっています。
Apache HTTP Server から Tomcatを呼びだすためには、8009ポート宛にAJP通信を行えば良いことが分かりました。

```
.... 省略 ...
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
.... 省略 ...
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
.... 省略 ...
```



Apache HTTP Server から、Tomcat へ通信するためには、`mod_proxy_ajp`や`mod_proxy`のモジュールが必要になります。
Apache HTTP Server のモジュール読み込み設定ファイルを確認します

```
less /etc/httpd/conf.modules.d/00-proxy.conf
```



下記のように、2種類のモジュールが有効になっていることが分かります。

```
# This file configures all the proxy modules:
LoadModule proxy_module modules/mod_proxy.so <============== mod_proxyが有効になっている
LoadModule lbmethod_bybusyness_module modules/mod_lbmethod_bybusyness.so
LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so
LoadModule lbmethod_heartbeat_module modules/mod_lbmethod_heartbeat.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so <====== mod_proxy_ajpが有効になっている
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_express_module modules/mod_proxy_express.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
LoadModule proxy_fdpass_module modules/mod_proxy_fdpass.so
LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_scgi_module modules/mod_proxy_scgi.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
```



では次に、どんなパスにアクセスされた時に、Tomcat と連携するのか、ProxyPass の設定を行います
以下の書式で設定する必要があります。

```
ProxyPass httpdでのパス ajp://サーバー名:8009/Tomcat上のパス/
```



例えば、Apache HTTP Server で受け付けたすべてのリクエストを Tomcat で処理したい場合は、次のようになります(httpd と Tomcat が同一ホストにある場合)

```
ProxyPass / ajp://localhost:8009/
```



また sample ディレクトリへのアクセスを Tomcat 上の example と連携したい場合は次のようになります。

```
ProxyPass /sample/ ajp://localhost:8009/example/
```



それでは、今回は全てのパスをProxyとして転送するために、以下の設定を入れましょう

```
echo "ProxyPass / ajp://localhost:8009/" > /etc/httpd/conf.d/proxy.conf
```



Apache HTTP Server の再起動を行います

```
systemctl restart httpd
```



Tomcat のテストページが、Apache の 80 ポートで表示されていればOKです
![1563681679573.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104247/4b3d326c-137a-615c-a1f7-c51206fa6c68.png)



### Oracle JDBC Driver の ダウンロード

Javaアプリケーションから、Oracle DB にアクセスを行うためには、Oracle JDBC Driver を Installする必要があります。

Oracle JDBC Driverには、大きく分けて以下2種類があります。

1. Oracle Call Interface(OCI) Driverを使って接続する。Oracle JDBC Driver と Oracle Instant Clientを導入する必要がある
2. Thin Driver を使って接続する。Javaのライブラリで接続するため、Oracle JDBC Driver の導入だけでOK



基本的に、Oracle Interface Driver の固有の機能(TAFなど)を使う必要がなければ、Thin Driverを使用します。
Oracle Call Interface Driver と Thin Driver の違いは以下の表に記載があります。
https://docs.oracle.com/cd/F19136_01/jjdbc/introducing-JDBC.html#GUID-696E8F49-4B61-4520-8557-318320CE7A66

また、My Oracle Support の Knowledge ページに、Oracle JDBC Driver の説明や、Install方法、バージョン互換性などが記載されているため、一度読んでおくと勉強になります。(My Oracle Supportのアカウントが必要になります)
https://support.oracle.com/epmos/faces/DocumentDisplay?id=1999901.1

OTN(Oracle Technology Network) から、Oracle JDBC Driver の Jar ファイルを ダウンロードします。以下のページから、ダウンロードしたいバージョンを選択します。基本的には最新で問題ないと思います。
https://www.oracle.com/technetwork/database/application-development/jdbc/downloads/index.html

ojdbc10-full.tar.gz ファイルをダウンロードして、OCI上のCentOSインスタンスに格納します。



tar.gz ファイルを解凍します

```
cd /home/opc
tar xfvz ojdbc10-full.tar.gz
```



今回の例では、以下のディレクトリに解凍をしています。

```
$ ls -la /home/opc/ojdbc10-full 
total 8668 
drwxr-xr-x. 2 opc opc     211 Apr 29 22:15 .
drwx------. 7 opc opc     183 Jul 20 03:00 ..
-rw-r--r--. 1 opc opc    2625 Apr 29 22:15 README.txt
-r-xr-xr-x. 1 opc opc   11596 Apr 24 21:07 ojdbc.policy
-r--r--r--. 1 opc opc 4243140 Apr 24 21:07 ojdbc10.jar
-r--r--r--. 1 opc opc  144681 Apr 24 21:07 ons.jar
-r--r--r--. 1 opc opc  306004 Apr 24 21:07 oraclepki.jar
-r--r--r--. 1 opc opc 1661488 Apr 24 21:07 orai18n.jar
-r--r--r--. 1 opc opc  205154 Apr 24 21:07 osdt_cert.jar
-r--r--r--. 1 opc opc  306854 Apr 24 21:07 osdt_core.jar
-r-xr-xr-x. 1 opc opc   29205 Apr 24 21:07 simplefan.jar
-r--r--r--. 1 opc opc 1680074 Apr 24 21:07 ucp.jar
-r--r--r--. 1 opc opc  262664 Apr 24 21:07 xdb.jar
```



### (Skip)Oracle Instant ClientのInstall

今回の手順では、Oracle DB に接続するための、Oracle Instant Client はインストールしません。

JDBC Oracle Call Interface Driver 固有の機能を使用したい場合は、Oracle Instant Client を インストールする必要がありますので、ご注意ください。



## ATP の Wallet をダウンロード

rootユーザーとなり、ATPから wallet をダウンロードして、以下のディレクトリに解凍します。

```
sudo su -
mkdir /opt/wallet
cp -p /home/opc/Wallet_atpdataname.zip /opt/wallet/
cd /opt/wallet/
unzip Wallet_atpdataname.zip
rm -f Wallet_atpdataname.zip
```



以下のディレクトリ構成になっていればOKです

```
# ls -la /opt/wallet
total 36 
drwxr-xr-x. 2 root root  148 Jul 20 16:27 .
drwxr-xr-x. 4 root root   30 Jul 20 16:26 ..
-rw-r--r--. 1 root root 6661 Jul 20 16:27 cwallet.sso
-rw-r--r--. 1 root root 6616 Jul 20 16:27 ewallet.p12
-rw-r--r--. 1 root root 3242 Jul 20 16:27 keystore.jks
-rw-r--r--. 1 root root   87 Jul 20 16:27 ojdbc.properties
-rw-r--r--. 1 root root  114 Jul 20 16:27 sqlnet.ora
-rw-r--r--. 1 root root 1744 Jul 20 16:27 tnsnames.ora
-rw-r--r--. 1 root root 3335 Jul 20 16:27 truststore.jks
```



## ATPにSampleDataを格納

ATPに接続して、以下のSQLを流すことでSampleDataを格納します。
自分の場合は、Vistual Studio Code から接続を行ってSQLを流しています。
以下のURLに導入方法を記載しています。

https://qiita.com/sugimount/items/a71f470e9dc59fcd4957

```
-- Create a new Relational table with 3 columns

CREATE TABLE SAMPLE_TABLE 
(
  id VARCHAR2(255) NOT NULL,
  name VARCHAR2(1024)
);

-- Insert rows in a Table

INSERT INTO SAMPLE_TABLE 
VALUES
(
  '001',
  'suzuki'
);

-- Insert rows in a Table

INSERT INTO SAMPLE_TABLE 
VALUES
(
  '002',
  'satou'
);

-- Insert rows in a Table

INSERT INTO SAMPLE_TABLE 
VALUES
(
  '003',
  'tanaka'
);
```





# Javaアプリケーション編

## 純粋なJavaアプリから接続

動作確認のために、Tomcat を使用しない純粋なJavaアプリケーションから動作確認をします。

```java
mkdir /home/opc/java/
cat <<'EOF' > /home/opc/java/SampleRun.java
import java.sql.*;

public class SampleRun {
    public static void main(String[] args) {
        final String path = "jdbc:oracle:thin:@atpdataname_high";
        final String id = "ADMIN";  //ID
        final String pw = "passwordPassword1";  //password

        try (
            Connection conn = DriverManager.getConnection(path, id, pw);
            Statement  stmt = conn.createStatement();
            ResultSet  rs   = stmt.executeQuery("SELECT NAME FROM SAMPLE_TABLE");
        ) {
            while (rs.next()) {
                String cn = rs.getString("name");
                System.out.println("Name => " + cn);
            }
        } catch(SQLException ex) {
            ex.printStackTrace();  //Error
        }
    }
}
EOF
```



コンパイル

```
javac /home/opc/java/SampleRun.java
```



実行
Walletの格納先を環境変数に指定

```
cd /home/opc/java

export TNS_ADMIN=/opt/wallet

java -classpath /home/opc/ojdbc10-full/ojdbc10.jar:\
/home/opc/ojdbc10-full/ucp.jar:\
/home/opc/ojdbc10-full/oraclepki.jar:\
/home/opc/ojdbc10-full/osdt_core.jar:\
/home/opc/ojdbc10-full/osdt_cert.jar:. \
SampleRun
```



実行例

```
# java -classpath /home/opc/ojdbc10-full/ojdbc10.jar:\
> /home/opc/ojdbc10-full/ucp.jar:\
> /home/opc/ojdbc10-full/oraclepki.jar:\
> /home/opc/ojdbc10-full/osdt_core.jar:\
> /home/opc/ojdbc10-full/osdt_cert.jar:. \
> SampleRun
Name => satou 
Name => tanaka
Name => suzuki
```



## Apache HTTP Server + Apache Tomcatから接続

Tomcat の webapps ディレクトリは、下のディレクトリになっています

```
/opt/apache-tomcat-9.0.22/webapps
```



demoアプリ作成のために、ディレクトリを作成します

```
mkdir /opt/apache-tomcat-9.0.22/webapps/demo
mkdir /opt/apache-tomcat-9.0.22/webapps/demo/WEB-INF
mkdir /opt/apache-tomcat-9.0.22/webapps/demo/WEB-INF/lib
mkdir /opt/apache-tomcat-9.0.22/webapps/demo/WEB-INF/classes
```



Tomcat の Servlet から、ATPに接続するための demo アプリを格納します

```java
cat <<'EOF' > /opt/apache-tomcat-9.0.22/webapps/demo/WEB-INF/classes/DemoServlet.java
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import java.sql.DatabaseMetaData;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class JDBCSample_Servlet
 */
@WebServlet("/demo")
public class DemoServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;     
    /**
     * @see HttpServlet#HttpServlet()
     */
    public DemoServlet() {
        super();
    }

  /**
   * Method to get a connection to the Oracle Database and perform few 
   * database operations and display the results on a web page. 
   */
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
           throws ServletException, IOException {
    PrintWriter out = response.getWriter();
    out.print("Sample JDBC Servlet\n");
    try {
      // Get a context for the JNDI look up
      DataSource ds = getDataSource();
      // With AutoCloseable, the connection is closed automatically.
      try (Connection connection = ds.getConnection()) {
        out.print("The database user is " 
           + executeBusinessLogicOnDatabase(connection, out));
        out.print("\n Sample JDBC Servlet Request was successful");
        response.setStatus(200);
      }
    } catch (Exception e) {
      response.setStatus(500);
      response.setHeader("Exception", e.toString());
      out.print("\n Web Request failed");
      out.print("\n "+e.toString());
      e.printStackTrace();
    }
  }

 /*
  * Method to create a datasource after the JNDI lookup
  */

  private DataSource getDataSource() throws NamingException {
    Context ctx;
    ctx = new InitialContext();
    Context envContext = (Context) ctx.lookup("java:/comp/env");
    // Look up a data source
    javax.sql.DataSource ds
          = (javax.sql.DataSource) envContext.lookup ("tomcat/UCP_atp");
    return ds;
  }

 /*
  * Method to showcase database operations using the database connection 
  */
  private String executeBusinessLogicOnDatabase(Connection conn, 
    PrintWriter out) throws SQLException {
    // Get the JDBC driver name and version 
    DatabaseMetaData dbmd = conn.getMetaData();       
    out.println("Driver Name: " + dbmd.getDriverName());
    out.println("Driver Version: " + dbmd.getDriverVersion());
    
    String user = "";	
    String query = "SELECT NAME FROM SAMPLE_TABLE"; 
    Statement stmt = conn.createStatement(); 
    ResultSet rs = stmt.executeQuery(query); 
    while (rs.next()) { 
     user = user + " " + rs.getString(1);
    }          
    stmt.close();
    rs.close();
    return user;
  }

  public void destroy() { }
  
}
EOF
```



web.xml を作成します

```
cat <<'EOF' > /opt/apache-tomcat-9.0.22/webapps/demo/WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?> 
<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">

    <display-name>Demo Application</display-name>
    <description>
        This is a demo web application.
    </description>

    <servlet>
        <servlet-name>DemoServlet</servlet-name>
        <servlet-class>DemoServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>DemoServlet</servlet-name>
        <url-pattern>/demo</url-pattern>
    </servlet-mapping>

</web-app>
EOF
```



コンパイルします

```
javac -classpath /opt/apache-tomcat-9.0.22/lib/servlet-api.jar /opt/apache-tomcat-9.0.22/webapps/demo/WEB-INF/classes/DemoServlet.java
```



Oracle JDBC Driverを格納します

```
cp /home/opc/ojdbc10-full/* /opt/apache-tomcat-9.0.22/webapps/demo/WEB-INF/lib
```



Tomcat から ATP に接続するために、context.xml を新規作成し、Resouce を定義します。

```
mkdir /opt/apache-tomcat-9.0.22/webapps/demo/META-INF
```

```
cat <<'EOF' > /opt/apache-tomcat-9.0.22/webapps/demo/META-INF/context.xml
<Context> 
<Resource name="tomcat/UCP_atp" auth="Container"
   factory="oracle.ucp.jdbc.PoolDataSourceImpl"
   type="oracle.ucp.jdbc.PoolDataSource"
   description="UCP Pool in Tomcat"
   connectionFactoryClassName="oracle.jdbc.pool.OracleDataSource"
   minPoolSize="5"
   maxPoolSize="50"
   initialPoolSize="15"
   user="ADMIN"
   password="passwordPassword1"
   url="jdbc:oracle:thin:@atpdataname_high?TNS_ADMIN=/opt/wallet"
 />
</Context>
EOF
```



JAVA_OPTS を編集し、fanEnabledを無効化します

```
vim /opt/apache-tomcat-9.0.22/bin/catalina.sh
```

```
JAVA_OPTS="$JAVA_OPTS $JSSE_OPTS"
JAVA_OPTS="$JAVA_OPTS -Doracle.jdbc.fanEnabled=false"

↑追加
```



Tomcat 再起動

```
systemctl restart tomcat
```



http://ipaddress/demo/demo 
にアクセスして、Demoページが表示されていることを確認します。
The database user is ...  と記載されている箇所が、ATPか取得してきたUserを表示しています

![1563683485361.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104247/9316a0c0-812b-c7bb-d7c0-d5e7ea1a5476.png)





# 参考URL

Oracle JDBC Driverについて
https://support.oracle.com/epmos/faces/DocumentDisplay?id=1999901.1

Java開発者ガイド
https://docs.oracle.com/cd/F19136_01/jjdev/index.html

JDCB開発者ガイド
https://docs.oracle.com/cd/F19136_01/jjdbc/index.html

Java Connectivity to ATP
https://www.oracle.com/technetwork/database/application-development/jdbc/documentation/atp-5073445.html

Apache httpd と Tomcat を連携させる方法
https://weblabo.oscasierra.net/tomcat-mod-proxy-ajp/

GitHubでJavaアプリケーション
https://github.com/syobochim/code19-coding-hol/tree/master/spring-boot

上記GitHubの解説ブログ
https://syobochim.hatenablog.com/entry/2019/05/20/002555

IntelliJ IDEA で ATP に接続
https://syobochim.hatenablog.com/entry/2019/04/11/082800

Autonomous DB(ADW/ATP) に Java の JDBC Thin Driver で接続してみる。(OCI, Oracle Cloud Infrastructure)
https://qiita.com/ora_gonsuke777/items/91ec0e15848a78ede385