感謝のプログラミング 10000時間

たどり着いた結果(さき)は、感謝でした。

JBossでデータソースを使ってDBに接続して値を取得するサンプルと定義ファイルの書き方など。

<スポンサーリンク>

最初に、今回のサンプルで使うテーブルを用意する。

CREATE TABLE book 
(
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(100),
author VARCHAR(100)
);

次に、データを入れてみる

INSERT INTO book(title,author)
VALUES('GravityPierrot','Kotaro Isaka');

で、とりあえず、シンプルなPOJO(Plain Old Java Object)、要は普通のJavaで値を取り出してみる。

package com.test.db;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class SimpleMySQLConnect {
	public static void main(String[] args) {
		String driverName = "com.mysql.jdbc.Driver";
		String url = "jdbc:mysql://localhost:3306/test";
		String server = "localhost";
		String user = "root";
		String pass = "1234";
		String title = null;
		String author = null;
		Connection con = null;

		try {
			Class.forName(driverName);
			con = DriverManager.getConnection(url,user,pass);
			Statement stmt = con.createStatement();
			String sql = "select * from book";
			ResultSet rs = stmt.executeQuery(sql);
			while (rs.next()) {
				title = rs.getString("title");
				author = rs.getString("author");
				System.out.println("タイトルは:" + title + ",著者は:" + author);
			}
			rs.close();
			stmt.close();
			con.close();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			if (con != null) {
				try {
					con.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

実行した結果は

タイトルは:GravityPierrot,著者は:Kotaro Isaka

のように、ちゃんとJavaでデータを取得することができた。

さて、ここでINSERTした値をJBossのMBeanをinvokeして取り出してみたい。

まずはデータソースファイルを作る。
これは、
JBOSS_HOME/server/test(設定セット)/deploy以下に置く。
「~-ds.xml」という名前にすることがポイント。
deploy以下の~ds.xmlはJBossが「データソースを定義してるんだな」と判断して勝手に読み込んでくれる。
ここでは「test-ds.xml」というファイルを作成する。
こんな感じ。これ自体は、同じディレクトリにあるhsqldb-ds.xmlなんかをパクって作ればいい。

<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
   <local-tx-datasource>
      <jndi-name>TestDS</jndi-name>
      <connection-url>jdbc:mysql://localhost:3306/test</connection-url>
      <driver-class>com.mysql.jdbc.Driver</driver-class>
      <user-name>root</user-name>
      <password>1234</password>
      <min-pool-size>5</min-pool-size>
      <max-pool-size>20</max-pool-size>
      <idle-timeout-minutes>0</idle-timeout-minutes>
      <track-statements/>
      <security-domain>TestDbRealm</security-domain>
      <prepared-statement-cache-size>32</prepared-statement-cache-size>
      <metadata>
         <type-mapping>TEST SQL</type-mapping>
      </metadata>
<!--
      <depends>jboss:service=Hypersonic,database=localDB</depends>
-->
   </local-tx-datasource>   
</datasources>

さて、次にsarディレクトリを作る。
JBOSS_HOME/server/test(設定セット)/deploy以下に
db-test.sarディレクトリを作成する。

で、db-test.sar/META-INFというディレクトリを作ってその下に、
/deploy/META-INF/jboss-service.xmlを作成する。
jboss-service.xmlは、JBossのJMX-consoleからサービスを見えるようにするための定義などを書くんだけど、具体的にはこんな感じで書く。

<?xml version="1.0" encoding="UTF-8"?>
<server>
  <mbean code="com.test.db.DatabaseTestService" name="com.test.db:service=DatabaseTest">
    <attribute name="JndiName">java:TestDS</attribute>
  </mbean>
</server>

これで、JBoss側の設定はできた!
さて、肝心のコードだが、こんな感じ。
まずはMBeanのインターフェースを作る。

package com.test.db;

public interface DatabaseTestServiceMBean {
	String getJndiName();
	void setJndiName(String jndiName);
	void printDbValue();
	void start() throws Exception;
	void stop();
}

JBoss上で動かすサービスを作るときは、まずMBeanのインターフェースを作る→それから、そのMBeanのインターフェースをimplementsするクラスを作る。
大事なのは、このように実装のルールを守ることと、命名のルールを守ることだ。
そこをちゃんと守れば、JBossはJavaプログラマをしっかりとサポートしてくれる。

JBossでサービスを作るには、~ServiceMBeanというインターフェースを作るのが必須なんだけど、
ここでは必ず、start()メソッドとstop()メソッドが定義されていなければならない。
では、実装したクラス。

package com.test.db;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class DatabaseTestService implements DatabaseTestServiceMBean {
	private String jndiName;
	private DataSource ds;
	public String getJndiName() {
		return jndiName;
	}

	public void setJndiName(String jndiName) {
		this.jndiName = jndiName;
	}

	public void printDbValue() {
		Connection con = null;
		String title = null;
		String author = null;
		if (ds == null) {
			try {
				InitialContext ctx = new InitialContext();
				ds = (DataSource) ctx.lookup(jndiName);
				con = ds.getConnection();
				Statement stmt = con.createStatement();

				String sql = "select * from book"; //本当はベタ書きよくない
				ResultSet rs = stmt.executeQuery(sql);
				while (rs.next()) {
					title = rs.getString("title");
					author = rs.getString("author");
					System.out.println("(JBossでDBから取得した)タイトルは:" + title + ",著者は:" + author);
				}

			} catch (NamingException e) {
				e.printStackTrace();
			} catch (SQLException e) {

				e.printStackTrace();
			}
		}

	}

	public void start() throws Exception {
		System.out.println("Database Connection test on JBoss START!");
	}

	public void stop() {
		System.out.println("Database Connection test on JBoss STOP!");
	}

}

このJavaのjarを作成して、JBOSS_HOME/server/test(設定セット)/lib以下に配置。
一緒に、MySQLのドライバのjarも配置しなければならない。

この設定セット/lib以下に置いたjarファイルは、JBossが勝手に読み込んでくれる。

ここまでやったら、jmx-consoleにつないで、printDbValue()をinvokeしてみよう。
結果はこんな感じでコンソールに表示される。server.logにも残る。

2013-10-20 19:42:16,205 INFO  [STDOUT] (JBossでDBから取得した)タイトルは:GravityPierrot,著者は:Kotaro Isaka

これは、上のシンプルなJDBCでDBの値をとってきたときと同じで、JBoss上で走らせたJavaでDBに接続することができた!

やってるうちに出てきたエラー

--- MBeans waiting for other MBeans ---
ObjectName: com.test.db:service=DatabaseTest
  State: FAILED
  Reason: org.jboss.deployment.DeploymentException: No Attribute found with name
: jndiName

--- MBEANS THAT ARE THE ROOT CAUSE OF THE PROBLEM ---
ObjectName: com.test.db:service=DatabaseTest
  State: FAILED
  Reason: org.jboss.deployment.DeploymentException: No Attribute found with name
: jndiName

これは、jboss-service.xmlのattributeのところの先頭を小文字にしていたから。
正解はこんな感じなんだけど、

    <attribute name="JndiName">java:TestDS</attribute>

このJndiNameをjndiNameとしていたから、「見つからないよ!」と怒られた。

 ERROR [STDERR] javax.naming.NameNotFoundException: TestDS not bound

これは指定するJNDI名が間違っていた。以下のように間違っていた。

    <attribute name="JndiName">TestDS</attribute>

これは、java:TestDSとしなければいけなかった。
java:[JNDI名]」とする。

セキュリティドメインを設定していないにも関わらず、データソースのXMLに勝手に以下の記述をしていたら、ユーザーとパスワードが見つからないよ、と怒られた。当然だ。

<security-domain>TestDbRealm</security-domain>

JBoss Enterprise Application Platform6 構築・運用パーフェクトガイド

JBoss Enterprise Application Platform6 構築・運用パーフェクトガイド

JBoss徹底活用ガイド ーJava・オープンソース・JBoss Seam・JBoss AS

JBoss徹底活用ガイド ーJava・オープンソース・JBoss Seam・JBoss AS