关闭

手动绑定Context

标签: jbossjavaschemaapacheencoding测试
2814人阅读 评论(0) 收藏 举报
分类:
原创声明:
    本文章为原创,欢迎转载,但请给出本文链接,多谢合作。
    本文链接:http://blog.csdn.net/epinszteinic/archive/2011/01/25/6163196.aspx

手动绑定Context

为什么会有这个需求?

一个jboss的web应用,DataSource(以下简称DS)通过jboss的配置文件设置,如下:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
  <local-tx-datasource>
    <jndi-name>DS_abcd</jndi-name>
    。。。
  </local-tx-datasource>
</datasources>

众所周知,jboss启动很慢,测试很痛苦。我不需要跑画面,我只需要运行逻辑!!
怎么办?自己手动绑定DS!!

需要知道两个常量:
Context.INITIAL_CONTEXT_FACTORY = "java.naming.factory.initial"
Constant that holds the name of the environment property for specifying the initial context factory to use. The value of the property should be the fully qualified class name of the factory class that will create an initial context. This property may be specified in the environment parameter passed to the initial context constructor, an applet parameter, a system property, or an application resource file. If it is not specified in any of these sources, NoInitialContextException is thrown when an initial context is required to complete an operation. 

The value of this constant is "java.naming.factory.initial".

Context.URL_PKG_PREFIXES = "java.naming.factory.url.pkgs"
Constant that holds the name of the environment property for specifying the list of package prefixes to use when loading in URL context factories. The value of the property should be a colon-separated list of package prefixes for the class name of the factory class that will create a URL context factory. This property may be specified in the environment, an applet parameter, a system property, or one or more resource files. The prefix com.sun.jndi.url is always appended to the possibly empty list of package prefixes. 

The value of this constant is "java.naming.factory.url.pkgs".

在jboss的debug状态下,Context context = new InitialContext(),可以看到,context里的myProps设置有两个key,就是上面这两个常量。
它们的value是:
Context.INITIAL_CONTEXT_FACTORY => org.jnp.interfaces.NamingContextFactory
Context.URL_PKG_PREFIXES => org.jnp.interfaces
哦,原来jboss用的是org.jnp.interfaces.NamingContextFactory和org.jnp.interfaces包下的类。那么,我们就开始手动构造Context吧!
org.jnp.interfaces在哪里呢?在/JBOSS_HOME/client/jnp-client.jar里,另外还需要jboss-common-client.jar(需要里面的Logger)
好,把这两个jar包拷贝到classpath里。

System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES, "org.jnp.interfaces");
Context context = new InitialContext();
DataSource ds = .....; // DS的构造看下面
context.bind("DS_abcd", ds);

似乎失败了,报错:javax.naming.CommunicationException: Receive timed out [Root exception is java.net.SocketTimeoutException: Receive timed out]
恩,jboss似乎用RMI来取DS。。。怎么办?继续往下看。

JNDI(Java Naming and Directory Interface)是sun的一个规范,所以,必然有很多实现,jboss有一套实现,apache也有一套实现,等等。
但是,因为遵循了相同的规范,结果也必然是一致的!

所以,我们可以利用apache的实现,去替换掉jboss的实现!!
怎么替换?对,就是那两个常量!!改之:
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming");
那么org.apache.naming.java.javaURLContextFactory和org.apache.naming又是在哪里呢?
在naming-java-5.0.28.jar和naming-common-5.0.28.jar里。
这回应该正确绑定上了。

DS如何构造?debug一下jboss,看看真正的DS是哪个类,new出来就可以啦。
MysqlConnectionPoolDataSource ds_1 = new MysqlConnectionPoolDataSource();
ds_1.setURL(url); // jdbc:mysql://192.168.1.100/DBTEST1?characterEncoding=utf8
ds_1.setUser(dbUser);
ds_1.setPassword(dbPassword);

关于jndi-name,还有话要说。
典型的jndi-name像这样:
  java:/comp/evn/DS_abcd
InitialContext里有这么一段代码:
    private static String getURLScheme(String str) {
        int colon_posn = str.indexOf(':');
        int slash_posn = str.indexOf('/');

        if (colon_posn > 0 && (slash_posn == -1 || colon_posn < slash_posn))
            return str.substring(0, colon_posn);

        return null;
    }
只有冒号和斜杠都存在,并且冒号在斜杠之前的话,会把冒号前的"java"作为schema使用。
不管内部如何实现,schema有没有都无所谓。
所以,某种意义上说,"java:"神马的都只是浮云!!那个冒号,只是弄得好像操作系统里那个C盘盘符后面的冒号一样,跟普通的字符没有任何区别。
如果要创建上面那个典型的jndi-name的Context,应该怎么做?
Context就像目录结构一样,而jndi名则以斜杠"/"为目录分隔符。
所以,就像创建文件夹一样,先父后子。

context.createSubcontext("java:");// 创建目录java:
context.createSubcontext("java:/comp");// 创建目录java:/comp
context.createSubcontext("java:/com/evn");// 创建目录java:/comp/evn
context.bind("java:/com/evn/DS_abcd", ds);// 创建文件java:/comp/evn/DS_abcd

或者
context.createSubcontext("java:").createSubcontext("comp").createSubcontext("evn").bind("DS_abcd", ds);

其实,createSubcontext会返回一个Context对象,就是刚刚创建的目录对象。

下面这样将会出错:
context.createSubcontext("java:");
context.createSubcontext("comp");
context.createSubcontext("evn");
context.bind("java:/com/evn/DS_abcd", ds);
结果是什么呢?就是你在根目录下,创建了3个并列的目录:"java:","comp","evn"。然后企图往一个不存在的目录"java:/comp/evn"上创建文件"DS_abcd"。当然会失败。

以上的说明和代码,都是基于jre1.5做的(jre6也测试通过)。

最后献上查看Context里所有目录所有绑定对象的信息的方法:
// 调用方法
showAll(context, 0);

private static void showAll(Context context, int step) throws NamingException {
    NamingEnumeration<NameClassPair> ne = context.list("");
    while(ne.hasMore()){
        NameClassPair e = ne.next();
        System.out.println(makeSpace(step) + e);
        Object o = context.lookup(e.getName());
        if(o instanceof Context){
            showAll((Context)o, step + 1);
        }
    }
}

private static String makeSpace(int step){
    String ret = "";
    for(int i=0;i<step;i++){
        ret += "  ";
    }
    return ret;
}

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:92395次
    • 积分:735
    • 等级:
    • 排名:千里之外
    • 原创:16篇
    • 转载:0篇
    • 译文:0篇
    • 评论:7条
    最新评论
    CSDN