/*
* Copyright (c) 2007 NTT DATA Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jp.terasoluna.fw.message;
import java.util.List;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
/**
* DBからメッセージリソースを取得するDBMessageResourceDAOの実装クラス。
* 本クラスは、メッセージリソースを格納したDBから検索SQL文を使用し、
* メッセージリソースをDBMessageオブジェクトのリストとしてまとめ、返却する。
* <br><br>
* <strong>使用方法</strong>
* <br>
* このクラスを使用するにはアプリケーションコンテキスト起動時にDAOとして
* 認識させる必要がある。<br>
* <br>
* <strong>設定例</strong><br>
* DAOの実装クラスとして本クラスを使用する場合、Bean定義ファイルに
* 以下の記述をする。<br>
* <pre>
* <bean id = "dBMessageResourceDAO"
* class = "jp.terasoluna.fw.message.DBMessageResourceDAOImpl">
* <property name = "dataSource">
* <ref bean = "dataSource"></ref>
* </property>
* </bean>
* </pre>
*
* <strong>解説</strong><br>
* <bean>要素のid属性にDBMessageResourceDAOを指定し、<bean>要素内
* <property>要素にはdataSourceを設定する。<br>
*
* <h3>検索SQL文について</h3>
*
* DBからメッセージリソースを取得する検索SQL文には初期値が与えられている。
* デフォルトの検索SQL文は
* <pre>SELECT CODE,MESSAGE FROM MESSAGES</pre>
* である。
* デフォルトの検索SQL文を使用する場合、DBのカラム名は以下の通りとなる。<br>
* テーブル名 = MESSAGES<br>
* メッセージコードを格納するカラム名 = CODE<br>
* メッセージ本文を格納するカラム名 = MESSAGE<br>
* 尚、デフォルトの検索SQL文を使用する場合、ロケール対応は行われない。
* ロケール対応する場合は、下記、検索SQL文の変更が必要となる。
*
*
* <h4>検索SQL文の変更1</h4>
* この変更方法は検索SQL文のフォーマットにしたがって、テーブル名及び各カラム名を
* 独自に指定し、本クラスの機能によって、検索SQL文を生成する方法である。この方法
* を実施することで、以下のことが可能になる。<br>
* 1.DBのテーブル名及び各カラム名の自由な設定<br>
* 2.ロケール対応<br>
* 検索SQL文のフォーマット
* <pre>SELECT メッセージコードのカラム名 , 言語コードのカラム名 , 国コードのカラム名, バリアントコードのカラム名 ,メッセージ本体のカラム名 FROM テーブル名
* </pre>
* テーブル名及び各カラム名の全てもしくは一部を指定することでDBのテーブル名及び
* カラム名を自由に設定出来る。一部の値のみを指定した場合、指定されていない値は
* 上記デフォルトの検索SQL文の値が使用される。<br>
* 又、言語コードのカラム名、国コードのカラム名、バリアントコードのカラム名を
* 指定するし、各カラムを有効にすることにより、これらのコードによるロケールの
* 判別が可能となる。
* これらの値は本クラス内に実装されている各々のセッターを利用する事で変更出来る。
* <br>
* <br>
* <strong>設定例</strong><br>
* Bean定義ファイル内で以下のような記述をする。<br>
* テーブル名及び各カラム名の全てを独自に設定する場合。
*
* <pre>
* <bean id = "DBMessageResourceDAO"
* class = "jp.terasoluna.fw.message.DBMessageResourceDAOImpl">
* <ref bean = "dataSource"></ref>
* </property>
* <property name = "tableName">
* <value>DBMESSAGES</value>
* </property>
* <property name = "codeColumn">
* <value>BANGOU</value>
* </property>
* <property name = "languageColumn">
* <value>GENGO</value>
* </property>
* <property name = "countryColumn">
* <value>KUNI</value>
* </property>
* <property name = "variantColumn">
* <value>HOUGEN</value>
* </property>
* <property name = "messageColumn">
* <value>MESSAGE</value>
* </property>
* </bean>
* </pre>
*
* <bean>要素内<properities>要素のname属性に変更したいテーブル名や
* カラム名を指定し、value属性にて設定したい値を指定する。<br>
* <br>
* 上記設定により検索SQL文は
* <pre>SELECT BANGOU,GENGO,KUNI,HOUGEN,HONBUN FROM DBMESSAGES</pre>
* となる。<br>
* またDBのテーブル名及びカラム名は以下の通りとなる。 <br>
* テーブル名 = DBMESSAGES<br>
* メッセージコードを格納するカラム名 = BANGOU<br>
* メッセージの言語コードを格納するカラム名 = GENGO<br>
* メッセージの国コードを格納するカラム名 = KUNI<br>
* メッセージのバリアントコードを格納するカラム名 = HOUGEN<br>
* メッセージ本文を格納するカラム名 = HONBUN<br>
*
*
* <h4>検索SQL文の変更2</h4>
* この変更方法は本クラスの機能による検索SQL文の生成を行わずに、検索SQL文を独自
* に指定する方法である。WHERE句など検索SQL文のフォーマットでは対応出来ない
* クエリを利用する場合に有効である。<br>
* <br>
* <strong>設定例</strong><br>
* Bean定義ファイル内で以下のような記述をする。
* 検索SQL文及びテーブル名、各カラム名の全てを独自に設定する場合。<br>
* <br>
*
* <pre>
* <bean id = "DBMessageResourceDAO"
* class = "jp.terasoluna.fw.message.DBMessageResourceDAOImpl">
* <property name = "dataSource">
* <ref bean = "dataSource"></ref>
* </property>
* <property name = "findMessageSql">
* <value>
* SELECT BANGOU as CODE,HONBUN as MESSAGE FROM DBDATA WHERE CATEGORY = "DBMESSAGE"
* </value>
* </property>
* </bean>
* </pre>
*
* <bean>要素内<properities>要素のname属性に検索SQL文と使用する
* カラム名を指定し、value属性にて設定したいSQL文を指定する。<br>
* <br>
* SQL文で<br>
* 新規カラム名 as デフォルトのカラム名<br>
* とすることでDBのカラム名を変更することが出来る。
* ただし、デフォルトで値が渡されているメッセージコード及びメッセージの2カラム
* のみの対応となる。ロケール対応する場合、その他のカラムを設定する必要がある。
* その場合は検索SQL文の変更1に倣い、カラムを有効にする必要がある。<br>
* <br>
* 上記設定により検索SQL文は
* <pre>SELECT BANGOU as CODE,HONBUN as MESSAGE FROM DBDATA WHERE CATEGORY = "DBMESSAGE"
* </pre>
* となる。<br>
*
* またDBのテーブル名及びカラム名は以下の通りとなる。<br>
* テーブル名 = DBDATA<br>
* メッセージコードを格納するカラム名 = BANGOU<br>
* メッセージの言語コードを格納するカラム名 = null<br>
* メッセージの国コードを格納するカラム名 = null<br>
* メッセージのバリアントコードを格納するカラム名 = null<br>
* メッセージ本文を格納するカラム名 = HONBUN<br>
*
* @see jp.terasoluna.fw.message.DataSourceMessageSource
* @see jp.terasoluna.fw.message.DBMessage
* @see jp.terasoluna.fw.message.DBMessageQuery
* @see jp.terasoluna.fw.message.DBMessageResourceDAO
*
*/
public class DBMessageResourceDAOImpl extends JdbcDaoSupport implements
DBMessageResourceDAO {
/**
* メッセージを格納するDBのテーブル名。デフォルトはMESSAGES。
*/
protected String tableName = "MESSAGES";
/**
* メッセージコードを格納するDBのカラム名。デフォルトはCODE。
*/
protected String codeColumn = "CODE";
/**
* 言語コードを格納するDBのカラム名。デフォルトはnull。
*/
protected String languageColumn = null;
/**
* 国コードを格納するDBのカラム名。デフォルトはnull。
*/
protected String countryColumn = null;
/**
* バリアントコードを格納するDBのカラム名。デフォルトはnull。
*/
protected String variantColumn = null;
/**
* メッセージを格納するDBのカラム名。デフォルトはMESSAGE。
*/
protected String messageColumn = "MESSAGE";
/**
* 外部から設定されるDB検索時に使用されるSQL文。
* 設定されている場合、こちらが実行される。
*/
protected String findMessageSql = null;
/**
* ログクラス。
*/
private static Log log = LogFactory.getLog(DBMessageResourceDAOImpl.class);
/**
* メッセージを格納するDBのテーブル名を設定する。設定されていない場合は
* デフォルトの値MESSAGESが使用される。
*
* @param tableName
* メッセージを格納したDBのテーブル名。
*/
public void setTableName(String tableName) {
this.tableName = tableName;
}
/**
* メッセージコードを格納するDBのカラム名を設定する。設定されていない場合は
* デフォルトの値CODEが使用される。
*
* @param codeColumn
* メッセージコードを格納したDBのカラム名。
*/
public void setCodeColumn(String codeColumn) {
this.codeColumn = codeColumn;
}
/**
* 言語コードを格納するDBのカラム名を設定する。設定されていない場合は
* デフォルトの値nullが使用される。
*
* @param languageColumn
* 言語コードを格納したDBのカラム名。
*/
public void setLanguageColumn(String languageColumn) {
this.languageColumn = languageColumn;
}
/**
* 国コードを格納するDBのカラム名を設定する。設定されていない場合は
* デフォルトの値nullが使用される。
*
* @param countryColumn
* 国コードを格納したDBのカラム名。
*/
public void setCountryColumn(String countryColumn) {
this.countryColumn = countryColumn;
}
/**
* バリアントコードを格納するDBのカラム名を設定する。設定されていない場合は
* デフォルトの値nullが使用される。
*
* @param variantColumn
* バリアントコードを格納したDBのカラム名。
*/
public void setVariantColumn(String variantColumn) {
this.variantColumn = variantColumn;
}
/**
* メッセージを格納するDBのカラム名を設定する。設定されていない場合は
* デフォルトの値MESSAGEが使用される。
*
* @param messageColumn
* メッセージを格納したDBのカラム名。
*/
public void setMessageColumn(String messageColumn) {
this.messageColumn = messageColumn;
}
/**
* DBからメッセージリソースを検索するSQL文を設定する。設定されていない場合は
* makeSqlメソッドにて作成されたSQL文が実行される。
*
* @param findMessageSql
* 外部から設定されるDB検索時に使用されるSQL文。
*/
public void setFindMessageSql(String findMessageSql) {
this.findMessageSql = findMessageSql;
}
/**
* メッセージリソースを取得するRDBMSオペレーションクラス。
*/
protected DBMessageQuery dBMessageQuery = null;
/**
* DBMessageResourceDAOImplを生成する。
*/
protected DBMessageResourceDAOImpl() {
super();
}
/**
* DBよりメッセージリソースを取得するDBMessageQueryを生成する。
* コンストラクタに渡される値のうち、メッセージコードのカラム名、
* 言語コードのカラム名、国コードのカラム名はnullの場合がある。<br>
* nullが渡された場合、これらのカラムはDBに存在しないものとして処理される。
*
* @throws IllegalArgumentException
* DBとの接続が取得できなかった場合
*/
@Override
protected void initDao() {
DataSource dataSource = getDataSource();
if (dataSource == null) {
log.error("Missing dataSource in spring configuration file.");
throw new IllegalArgumentException("Missing dataSource in spring"
+ " configuration file.");
}
this.dBMessageQuery = new DBMessageQuery(dataSource, makeSql(),
codeColumn, languageColumn, countryColumn, variantColumn,
messageColumn);
}
/**
* DBから取得したメッセージリソースをDBMessageオブジェクトに格納し、リスト型
* で返却する。
*
* @return メッセージリソースのリスト
*/
@SuppressWarnings("unchecked")
@Override
public List<DBMessage> findDBMessages() {
// JDBCDaoSupportにてDBMessageQueryが必ず生成されるため、
// nullにはならない。
return dBMessageQuery.execute();
}
/**
* DBからメッセージリソースを取得するSQL文を生成する。
* SQL文生成前にカラム名及びテーブル名に不正な値が渡されていないかの
* チェックをする。必須カラム名(メッセージコード、メッセージ本体)
* とテーブル名はnullチェック及び空文字チェックを実施する。
* その他のカラム名は空文字チェックのみを実施する。
*
* @return DBからメッセージリソースを取得するSQL文。
* nullは返却しない。
*
*/
protected String makeSql() {
// カラム名チェック
checkRequiredColumnName(codeColumn, "codeColumn");
checkNotRequiredColumnName(languageColumn, "languageColumn");
checkNotRequiredColumnName(countryColumn, "countryColumn");
checkNotRequiredColumnName(variantColumn, "variantColumn");
checkRequiredColumnName(messageColumn, "messageColumn");
checkRequiredColumnName(tableName, "tableName");
// 外部からSQL文を指定された場合、そちらを使用する。
StringBuilder sql = null;
if (findMessageSql != null) {
sql = new StringBuilder(findMessageSql);
} else {
// SQL文の指定がない場合、新たに生成する。
sql = new StringBuilder("SELECT ");
sql.append(codeColumn);
sql.append(",");
// 言語コードのカラム名が、設定されていない場合は
// 言語コードを検索しない。
if (languageColumn != null) {
sql.append(languageColumn);
sql.append(",");
}
// 国コードのカラム名が、設定されていない場合は国コードを検索しない。
if (countryColumn != null) {
sql.append(countryColumn);
sql.append(",");
}
// バリアントコードのカラム名が、設定されていない場合は
// バリアントコードを検索しない。
if (variantColumn != null) {
sql.append(variantColumn);
sql.append(",");
}
sql.append(messageColumn);
sql.append(" FROM ");
sql.append(tableName);
}
if (log.isDebugEnabled()) {
log.debug("sql=[" + sql + "]");
}
return sql.toString();
}
/**
* 必須カラムのカラム名及びテーブル名をチェックする。
* nullチェック及び空文字チェックを実施する。
*
* @param value
* DBでのカラム名もしくはテーブル名
* @param columnName
* 検査対象のカラムもしくはテーブル
*/
protected void checkRequiredColumnName(String value, String columnName) {
// カラム名のエラーチェック。
// メッセージコードのカラム名がnullもしくは空文字の場合、エラーを返す。
if (value == null || "".equals(value)) {
log.error("illegalArgument: " + columnName + " is null or empty.");
throw new IllegalArgumentException("illegalArgument: " + columnName
+ " is null or empty.");
}
}
/**
* 必須カラム以外のカラム名をチェックする。 空文字チェックを実施する。
*
* @param value
* DBでのカラム名
* @param columnName
* 検査対象のカラム
*/
protected void checkNotRequiredColumnName(String value, String columnName) {
// カラム名のエラーチェック。
// メッセージコードのカラム名が空文字の場合、エラーを返す。
if ("".equals(value)) {
log.error("illegalArgument: " + columnName + " is empty.");
throw new IllegalArgumentException("illegalArgument: " + columnName
+ " is empty.");
}
}
}