/*
 * $Id: MySQLDocumentAccessor.java,v 1.8 2006/02/21 15:57:09 akabane Exp $
 * LOGICAL-PARADOX.ORG
 * Copyright (C)2005 satoshi akabane(akabane@logical-paradox.org)
 *
 */
package org.logical_paradox.koike.rss.index;

import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ResourceBundle;

import org.logical_paradox.common.charset.CharsetUtils;
import org.logical_paradox.common.sql.DbUtils;
import org.logical_paradox.common.util.StringUtils;
import org.logical_paradox.koike.core.document.Document;
import org.logical_paradox.koike.core.document.DocumentAccessor;
import org.logical_paradox.koike.rss.document.ContentsAttribute;
import org.logical_paradox.rss.rcm.accessor.mysql.validator.ConnectionValidation;
import org.logical_paradox.rss.rcm.accessor.mysql.validator.ConnectionValidator;
import org.logical_paradox.rss.rcm.df.DataFilter;
import org.logical_paradox.rss.rcm.df.DataFilterFactory;
import org.logical_paradox.rss.util.DocumentIdGenerator;

/**
 * mysql serverをストレージとするタイプのドキュメントアクセサ
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.8 $
 */
public class MySQLDocumentAccessor implements DocumentAccessor, MySQLInterfaceConstant {
	/** プロパティファイル */
	private final ResourceBundle resource;
	/** データフィルタ */
	private DataFilter filter;
	/** データベース接続 */
	private Connection con;
	/** データベース接続監視スレッド */
	private ConnectionValidator validator;
	/** ステートメント */
	private PreparedStatement pstmt;
	/** コンテンツのエンコーディング */
	private String encoding;
	/** コンテンツ取得時，エンコーディング変換を必要とするか */
	private boolean needToEncode = false;
	/**
	 * コンストラクタ．
	 * @param cv 接続検証スレッド
	 */
	public MySQLDocumentAccessor(ConnectionValidator vc) {
		validator = vc;
		// リソースファイルを読み込んでおく
		resource = ResourceBundle.getBundle("mysql");

		try {
			String jdbcDriverClass = resource.getString(PKEY_JDBC_DRIVER_CLASS);
			String connstr = resource.getString(PKEY_DOCUMENT_CONNECT_STR);
			String sql = resource.getString(PKEY_DOCUMENT_SEARCH_SQL);
			encoding = resource.getString(PKEY_ENCODING_CONTENTS);

			Class.forName(jdbcDriverClass);
			con = DriverManager.getConnection(connstr);
			pstmt = con.prepareStatement(sql);

			// データフィルタの生成
			filter = DataFilterFactory.getFilter(DataFilterFactory.GZIP);

			// データベース接続検証スレッドに接続監視を依頼する
			ConnectionValidation validation = new ConnectionValidation(con, "select count(*) from contents");
			validator.add(validation);

			// エンコーディング変換が必要かどうかの判定
			needToEncode = StringUtils.isEmpty(encoding) ? false : CharsetUtils.testNeedToEncode(encoding);
		} catch(Exception e) {
			tearDown();
			throw new IllegalArgumentException(e.getMessage());
		}
	}
	/**
	 * 指定キーに対応するドキュメントを取得する
	 * @param key キー
	 * @return 取得できたドキュメント(null: 該当なし)
	 * @throws Exception アクセス中に発生した例外
	 */
	public Document getDocument(String key) throws Exception {
		long docid = DocumentIdGenerator.getDocumentNo(key);
		Document document = null;

		synchronized(this) {
			ResultSet rs = null;
			try {
				pstmt.setLong(1, docid);

				rs = pstmt.executeQuery();
				if(rs.next()) {
					// 文書情報を取得す
					String sitename = rs.getString("sitename");
					String title = rs.getString("title");
					String url = rs.getString("url");
					Blob blob = rs.getBlob("contents");
					long length = blob.length();
					byte[] decompressedContents = filter.decompressContents(blob.getBytes(1L, (int)length));
					document = new Document();
					document.setDocument(
								needToEncode ?
								new String(decompressedContents, encoding) :
								new String(decompressedContents)
							);

					// コンテンツの属性を設定する
					ContentsAttribute attr = new ContentsAttribute();
					attr.setSitename(sitename);
					attr.setTitle(title);
					attr.setUrl(url);
					document.setAttribute(attr);
				}
			} finally {
				// リソースの回収
				DbUtils.closeResultSet(rs);
			}
		}
		return document;
	}
	/**
	 * ドキュメントリソースとの接続を閉じる
	 * @throws Exception クローズに失敗した際に発生する例外
	 */
	public void close() throws Exception {
		tearDown();
	}
	/**
	 * 接続が既にクローズされているかどうかを返す
	 * @return true:クローズされている / false:クローズされていない
	 */
	public boolean isClosed() {
		return con != null;
	}
	/**
	 * このインターフェースの終了処理を行なう．
	 */
	protected synchronized void tearDown() {
		DbUtils.closeStatement(pstmt);
		DbUtils.closeConnection(con);
	}
}
