JNDI コンテキスト作成してみる

先日の 記事で書いた 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;
   }
}