Apache Tomcat JDBC连接池接入MySQL数据库的NamingContext异常排解

在最近新部署的 -GWA2 in Java 的实例中,一切进展顺利,唯独在启用连接池访问 Tomcat 接管的 MySQL 数据库连接池时报错出异常。问题排解过程曲折,值得记录如下。服务器端日志信息如下:

java.lang.ClassCastException: class org.apache.naming.NamingContext cannot be cast to class javax.sql.DataSource (org.apache.naming.NamingContext is in unnamed module of loader java.net.URLClassLoader @5383967b; javax.sql.DataSource is in module java.sql of loader ‘platform’)

根据报错信息,找出报出问题的代码段如下:

if(this.hasSocketPool){
try{
    this.ctx = new InitialContext();
    this.ds = (DataSource)ctx.lookup(“java:comp/env/jdbc/”+this.myDb); //- error!
}
catch(Exception ex){
ex.printStackTrace();
}
}

就是在 ctx.lookup 返回对象进行强制类型(DataSource)转换时发送错误,抛出异常。

让人费解的是,这些代码在另外的运行时环境时,运行良好,难道跟新部署的环境有关? Tomcat 9.0+ / MySql 8.0+ / Java 11.0+ ?

报错信息的字面意思是 “A类不能强制转为B类(A类在未命名的加载器模块C中,B类在platform加载器的java.sql模块中)”。根据这些理解,大致是加载的类出现错乱了,于是沿着这个思路搜索一下网上其他同学的过往经历。

网上的资讯大致可以分为两种,一种是的确加载错了 org.apache.naming.* 相关的类,另外一种更大的可能是没有正确地在 Tomcat中配置 conf/server.xml 和 conf/web.xml ,这个参照 Tomcat的手册可以很快排除。

而第一种,加载错乱的情况,在GWA2 Java中,理论上不应该发生,因为我们在 inc/MySql.class 中明确地声明了加载如下类:

<%@page import=”javax.sql.DataSource,
java.sql.DriverManager,
java.sql.ResultSet,
java.sql.PreparedStatement,
javax.naming.Context,
javax.naming.InitialContext”%>

显然,其中并没有显示地加载任何 org.apache.naming 相关的类。尽管如此,我们还是在代码层面将类的调用再一次的明确下来,使用类的绝对路径以避免可能引起的混淆。改进后的代码为:

javax.naming.Context ctx = new javax.naming.InitialContext();
javax.naming.Context envCtx = (javax.naming.Context)ctx.lookup(“java:comp/env”);
envCtx = (javax.naming.Context)envCtx;
Object dsObj = envCtx.lookup(“jdbc/”+this.myDb);

很遗憾,带上绝对路径的类的声明与应用于事无补。

迫不得已,我们决定将部署归零,清空所有配置,重新下载最新版的 Tomcat, GWA2 Java等,然后重新安装,再次运行时,很遗憾,问题依旧!

几乎要放弃了,这种可能由于软件不匹配导致的问题,已经超出应用层和代码层的解决能力范畴。不能放弃,还有最后一招——分析源代码——还没使用。于是耐下心来,翻看 Tomcat 的API文档和 Java 的API 文档,逐个对象查询、分析,并在程序中逐个步骤进行断点测试。

javax.naming.NamingEnumeration dsList = envCtx.list(“jdbc/”);
debug(“dsObj:”+dsObj+” / “+dsObj.toString()+” ctx:”+ctx+” envctx:”+envCtx+” envctx.getEnv:”+envCtx.getEnvironment());
debug(“dsList:”+dsList+” mydb:”+this.myDb);
while(dsList.hasMore()){
    debug(“dsList: next:”+dsList.next());
}

-R/B2SR 
https://tomcat.apache.org/tomcat-9.0-doc/api/index.html

-R/72SS 
https://docs.oracle.com/en/java/javase/11/docs/api/java.naming/javax/naming/Context.html

进一步地日志输出中,我们看到了蛛丝马迹,在测试代码的第二步,原本应该返回 javax.naming.Context 对象的,却被 Tomcat 替换为 org.apache.naming.NamingContext. 所以后就几乎全都跑偏了。

Thu Jan 03 05:56:32 UTC 2019 dsObj:org.apache.naming.NamingContext@368631b7 / org.apache.naming.NamingContext@368631b7 ctx:javax.naming.InitialContext@4f52e2fc envctx:org.apache.naming.NamingContext@5b51d294 envctx.getEnv:{}
Thu Jan 03 05:56:32 UTC 2019 dsList:org.apache.naming.NamingContextEnumeration@152fa16f mydb:
Thu Jan 03 05:56:32 UTC 2019 dsList: next:userdb: org.apache.tomcat.dbcp.dbcp2.BasicDataSource
Thu Jan 03 05:56:32 UTC 2019 dsList: next:contentdb: org.apache.tomcat.dbcp.dbcp2.BasicDataSource
Thu Jan 03 05:56:32 UTC 2019 dsList: next:newsdb: org.apache.tomcat.dbcp.dbcp2.BasicDataSource

当我们调用 org.apache.naming.NamingContext 对象的方法 .getEnvironment 和 .list 进一步地输出时发现,配置信息 userdb, contentdb 和 newsdb 都在。 为何返回是一个包含列表的对象NamingContext 而不是一个 DataSource类型的数据?

谜底揭开!在当前运行时环境中关键变量 this.myDb 为空,当传入一个空值的 资源标识符(数据库资源)时,ctx.lookup 不是报错未找到,而是返回了DataSource的父类 NamingContext!

(DataSource)ctx.lookup(“java:comp/env/jdbc/”+this.myDb); //- error if empty myDb

修正方式:
1)在 GWA2 Java中对 this.myDb 做兼容,如果为空,则不启用默认数据库连接池的连接;
2)尝试递交 bug给 Apache Tomcat 开发组,修改报错信息或改进返回值类型,减少对用户可能产生误导的信息。
3) 尝试回复stackoverflow上类似悬而未决的一问题。

翻阅源代码,仔细查看API文档依然是解决问题的终极大杀器之一。

-GWA2 是”通用网络应用架构( General Web Application Architeture )”,基于 -GWA2 可以轻便构建各种网络应用程序,
包括复杂的在线购物商城、 旅游交易平台、社群或者社交网站和新闻资讯网站等,
也包括各种企事业单位网上门户,在线交互及服务作业系统等.
还可以包括为NativeApp做服务器端支持, 甚至是WebApp的全部.
-GWA2 是为数不多的支持跨开发语言的应用框架,目前支持 -Java, -PHP, -Perl, -Aspx and -Python .

-GWA2 is a “General Web Application Architecture” and based on -GWA2 developers can easily build a variety of network applications,
including complex online shopping malls, travel trading platforms, community or social networking sites and news information sites, etc.
Also the applications include various online portals of enterprises and institutions, online interaction and service operations systems.
Moreover it contains server-side support for NativeApp, or even all of the WebApp.
-GWA2 is one of the web frameworks which provide cross-language support for -Java, -PHP, -Perl, -Aspx and -Python at present.

-GWA2 is E.A.S.Y 
Easy Along, Swift Yield
轻松启动, 快速产出.

This entry was posted in -GWA2, 编程技术, 计算机技术 and tagged , , , , . Bookmark the permalink.

发表评论

电子邮件地址不会被公开。 必填项已用*标注