搞清楚一个关于“SAXException 未找到外部实体”的问题

一个应用程序新发版后,无法正常使用,从系统日志中查看下面的错误:

Caused by: org.xml.sax.SAXException: Fatal Error: URI=null Line=5: 未找到外部实体“http://ibatis.apache.org/dtd/sql-map-2.dtd”。

根据发版前后更改内容的对比,很快排查出原因可能出在新加的iBatis配置文件,原来的配置文件中DOCTYPE部分对应的:http://www.ibatis.com/dtd/sql-map-2.dtd,但具体原因是什么?

在使用SAX解析XML文件的时候,会根据DOCTYPE中的定义的DTD验证XML文件的合法性,但是iBatis的XML配置文件中DOCTYPE中DTD文件地址是个公网地址,那么对不能上互联网的情况下如何处理呢?

在IBM DW的这篇文章中有一个解决方案,可以在解析XML的时候设定自己的EntityResolver,将公网地址映射到本地地址。下面是iBatis 2.3中实现类关键代码:

public class SqlMapClasspathEntityResolver implements EntityResolver {

  private static final String SQL_MAP_CONFIG_DTD = "com/ibatis/sqlmap/engine/builder/xml/sql-map-config-2.dtd";
  private static final String SQL_MAP_DTD = "com/ibatis/sqlmap/engine/builder/xml/sql-map-2.dtd";

  private static final Map doctypeMap = new HashMap();

  static {
    doctypeMap.put("http://www.ibatis.com/dtd/sql-map-config-2.dtd".toUpperCase(), SQL_MAP_CONFIG_DTD);
    doctypeMap.put("http://ibatis.apache.org/dtd/sql-map-config-2.dtd".toUpperCase(), SQL_MAP_CONFIG_DTD);
    doctypeMap.put("-//iBATIS.com//DTD SQL Map Config 2.0//EN".toUpperCase(), SQL_MAP_CONFIG_DTD);
    doctypeMap.put("-//ibatis.apache.org//DTD SQL Map Config 2.0//EN".toUpperCase(), SQL_MAP_CONFIG_DTD);

    doctypeMap.put("http://www.ibatis.com/dtd/sql-map-2.dtd".toUpperCase(), SQL_MAP_DTD);
    doctypeMap.put("http://ibatis.apache.org/dtd/sql-map-2.dtd".toUpperCase(), SQL_MAP_DTD);
    doctypeMap.put("-//iBATIS.com//DTD SQL Map 2.0//EN".toUpperCase(), SQL_MAP_DTD);
    doctypeMap.put("-//ibatis.apache.org//DTD SQL Map 2.0//EN".toUpperCase(), SQL_MAP_DTD);
  }

  public InputSource resolveEntity(String publicId, String systemId)
      throws SAXException {

    if (publicId != null) publicId = publicId.toUpperCase();
    if (systemId != null) systemId = systemId.toUpperCase();

    InputSource source = null;
    try {
      String path = (String) doctypeMap.get(publicId);
      source = getInputSource(path, source);
      if (source == null) {
        path = (String) doctypeMap.get(systemId);
        source = getInputSource(path, source);
      }
    } catch (Exception e) {
      throw new SAXException(e.toString());
    }
    return source;
  }}

我们项目中iBatis用的是2.0的版本,是在iBatis加入Apache之前的版本,SqlMapClasspathEntityResolver里面只有对“www.ibatis.com”相关两个地址的映射,因此解析DOCTYPE里面为“http://ibatis.apache.org/dtd/sql-map-2.dtd”的XML文件时,就出现了上面提到的SAXException异常。

回想在开发环境上没有出现这个问题,原因是开发环境可以通过互联网直接下载http://ibatis.apache.org/dtd/sql-map-2.dtd这个文件。

Comments are closed.