先日の 記事で書いた AbstractXmlHandler と XmlParser を利用して、DBアクセスの為のJNDI、
コンテキスト作成をリメイクした。
バッチ処理と、どのWeb環境、Tomcat, JBoss , WebLogic でも自分で
DataSource コンテキストを管理すれば、どこでも1度作ったものを使い回せる夢を
持ったからである。
しかし、Tomcat は、JNDI コンテキストは、read-only であり、
起動後の書換えができない。
以下のように作成する。
// new InitialContext()実行前に、INITIAL_CONTEXT_FACTORY を設定
System.setProperty(Context.INITIAL_CONTEXT_FACTORY
,DataSourceFactory.class.getPackage().toString().replaceFirst("package ","")
+".DataSourceFactory"); // 適当なファクトリを用意する。
Context context = new InitialContext();
context.createSubcontext("java:comp").createSubcontext("env");
// DataSource生成定義した context.xml 読込→コンテキスト生成&バインド
XmlParser xmlparser = AbstractXmlHandler.createParser("context",new DbconHandler("/Context/Resource"));
try{
List<?> resouceList = (List<?>)xmlparser.parse();
Method methods = Resource.class.getMethods();
for(Object o : resouceList){
Resource resource = (Resource)o;
Properties prop = new Properties();
for(int i=0;i < methods.length;i++){
String methodName = methods[i].getName();
if (methodName.startsWith("get") && !methodName.equals("getClass")){
String key = methodName.substring(3,4).toLowerCase()+methodName.substring(4);
String v = (String)methods[i].invoke(resource,(Object)null);
if (v != null){
prop.setProperty(key,v);
}
}
}
String name = prop.getProperty("name");
String jndiName = "java:comp/env/"+name;
String nsp = name.split("/");
for(int i=0;i < (nsp.length-1);i++){
context.createSubcontext(nsp[0]);
}
// load する factory は、javax.naming.spi.ObjectFactory を implements してることが前提
Class<?> factorycls = Class.forName(prop.getProperty("factory"));
Method method = factorycls.getMethod("createDataSource",new Class{Properties.class});
context.bind(jndiName,method.invoke(null,new Object{prop}));
}
}catch(Exception e){
logger.error(e.getMessage(),e);
}
------------------------------------------
DbconHandler は、自分で用意する。Resource 等は次のとおり。
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
/**
* Resourceインスタンス生成の為のXML解析ハンドラ
*/
class DbconHandler extends AbstractXmlHandler{
private Stack<String> stack;
private String xPath;
private List<Map<String,String>> plist;
protected DbconHandler(String xPath){
this.xPath = xPath;
this.plist = new ArrayList<Map<String,String>>();
}
/*
* @see AbstractXmlHandler#result()
*/
@Override
public Object result() throws Exception{
List<Object> list = new ArrayList<Object>();
for(Map<String,String> map : this.plist){
Object obj = new Resource();
Method methods = Resource.class.getDeclaredMethods();
for(String f : map.keySet()){
String setter = "set"+f.substring(0,1).toUpperCase()+f.substring(1);
for(int k=0;k < methods.length;k++){
if (methods[k].getName().equals(setter)){
// Resource.class は、String フィールドのみである
methods[k].invoke(obj,map.get(f));
}
}
}
list.add(obj);
}
return list;
}
@Override
public void startElement(String uri,String localName,String name,Attributes attributes)
throws SAXException{
this.stack.push(name);
StringBuffer sb = new StringBuffer();
for(String s : this.stack){
sb.append("/"+s);
}
if (this.xPath.equals(sb.toString())){
int length = attributes.getLength();
if (length > 0){
Map<String,String> attributeMap = new HashMap<String,String>();
for(int i=0;i < length;i++){
attributeMap.put(attributes.getQName(i),attributes.getValue(i));
}
this.plist.add(attributeMap);
}
}
}
@Override
public void endElement(String uri,String localName,String name) throws SAXException{
this.stack.pop();
}
@Override
public void startDocument() throws SAXException{
this.stack = new Stack<String>();
}
@Override
public void endDocument() throws SAXException{
}
}
--------
/**
* Resource tag Bean
*/
final class Resource{
private String factory;
private String name;
private String type;
private String defaultAutoCommit;
private String defaultReadOnly;
private String defaultTransactionIsolation;
private String defaultCatalog;
private String driverClassName;
private String maxActive;
private String maxIdle;
private String minIdle;
private String initialSize;
private String maxWait;
private String testOnBorrow;
private String testOnReturn;
private String timeBetweenEvictionRunsMillis;
private String numTestsPerEvictionRun;
private String minEvictableIdleTimeMillis;
private String testWhileIdle;
private String password;
private String url;
private String username;
private String validationQuery;
private String accessToUnderlyingConnectionAllowed;
private String removeAbandoned;
private String removeAbandonedTimeout;
private String logAbandoned;
private String poolPreparedStatements;
private String maxOpenPreparedStatements;
private String connectionProperties;
private String auth;
public Resource(){}
// setter , getter が存在する
}
-------------------------------------
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;
public class DataSourceFactory extends org.apache.commons.dbcp.BasicDataSourceFactory
implements InitialContextFactory{
private static DatabaseContext instance;
@Override
public Context getInitialContext(Hashtable<?,?> env) throws NamingException{
if (instance==null){
instance = new DatabaseContext();
if (env==null);
}
return instance;
}
}