公司内有两个 Java web 应用,最近需要去客户处展示,需要安装在一台笔记本电脑上。 两个 Java web 应用,都有些年头了,最初使用 Java 1.6 开发,现使用 Java 8 重新编译。
有一台 16G 内存、500G 硬盘的笔记本,惠普战66。尝试将两个Java web 应用安装在此电脑里同一个 tomcat 内。
想着很简单,打包成 war 文件(分别是 zsso.war, pkg.war), 复制到 C:\java\apache-tomcat-9.0.52\webapps 目录下,改些配置文件,启动 tomcat 的 bin\startup.bat , 应该就行了。
其中,zsso.war 是公司的一个单点登录软件产品(Single Sign on, 缩写为 sso),自带用户登录、权限配置等功能。此次是 pkg.war 软件系统的更改时间紧张,不想再去做权限配置功能,想着可以借用 zsso.war 的功能,节省点开发时间,就算免费送客户一套单点登录系统好了。节省了开发时间,也是好的。
结果运行报错,pkg 能启动起来;zsso 启动失败;tomcat 自带的 docs, examples, host-manager,manager,ROOT 几个应用也启动失败。
报错信息相同,均为 XML 解析的 class 找不到:
1 org.apache.catalina.LifecycleException: Failed to initialize component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/zsso]]
2 at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:112)
3 at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:140)
4 at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:752)
5 at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:728)
6 at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734)
7 at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:986)
8 at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1857)
9 at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
10 at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
11 at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
12 at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
13 at java.base/java.lang.Thread.run(Thread.java:832)
14 Caused by: javax.xml.parsers.FactoryConfigurationError: Provider org.apache.xerces.jaxp.SAXParserFactoryImpl not found
15 at java.xml/javax.xml.parsers.FactoryFinder.newInstance(FactoryFinder.java:194)
16 at java.xml/javax.xml.parsers.FactoryFinder.newInstance(FactoryFinder.java:147)
17 at java.xml/javax.xml.parsers.FactoryFinder.find(FactoryFinder.java:226)
18 at java.xml/javax.xml.parsers.SAXParserFactory.newInstance(SAXParserFactory.java:219)
19 at org.apache.tomcat.util.digester.Digester.getFactory(Digester.java:474)
20 at org.apache.tomcat.util.digester.Digester.getParser(Digester.java:665)
21 at org.apache.catalina.startup.ContextConfig.init(ContextConfig.java:730)
22 at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:310)
23 at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:94)
24 at org.apache.catalina.util.LifecycleBase.setStateInternal(LifecycleBase.java:395)
25 at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:108)
26 ... 11 more
27 Caused by: java.lang.ClassNotFoundException: org/apache/xerces/jaxp/SAXParserFactoryImpl
28 at java.base/java.lang.Class.forName0(Native Method)
29 at java.base/java.lang.Class.forName(Class.java:427)
30 at java.xml/javax.xml.parsers.FactoryFinder.getProviderClass(FactoryFinder.java:119)
31 at java.xml/javax.xml.parsers.FactoryFinder.newInstance(FactoryFinder.java:183)
32 ... 21 more
查 tomcat 文档,http://tomcat.apache.org/tomcat-9.0-doc/class-loader-howto.html,里面专门有一章节 "XML Parsers and Java"。虽然没看明白,这个与我们此次报错有什么关联。但猜测,Java 中的 XML 解析相关类,与 class loader 紧密相关。
经调查,pkg.war 中使用了 SAX XML 解析,而 zsso.war 中没有使用。
推测 pkg.war 中使用了 SAX XML 解析,用了某个特定方式,导致影响了 zsso.war,也影响了 tomcat 自带的几个 web 应用及示例。
查找代码,有如下:
System.setProperty("javax.xml.parsers.SAXParserFactory", "org.apache.xerces.jaxp.SAXParserFactoryImpl");
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
parserFactory.setValidating(false);
parserFactory.setNamespaceAware(false);
RefrenceHandler mySaxParser = new RefrenceHandler();
SAXParser parser = parserFactory.newSAXParser();
parser.parse(refIs, mySaxParser);
经测试,去掉第一行代码,问题解决。
最后回顾,代码行:
System.setProperty("javax.xml.parsers.SAXParserFactory", "org.apache.xerces.jaxp.SAXParserFactoryImpl");
影响了 tomcat 中的多个 war 应用,也影响了 tomcat 自带的几个 web 应用及示例。
之前 Java 1.6 时,需要此行代码。最新的 Java 8 ,不需要此行代码。
因此,Java web 代码中,应避免/减少使用 System.setProperty(...);