日积月累
-
搞清楚一个关于“SAXException 未找到外部实体”的问题
2009-12-15
一个应用程序新发版后,无法正常使用,从系统日志中查看下面的错误:
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);
}
/**
* Converts a public DTD into a local one
*
* @param publicId Unused but required by EntityResolver interface
* @param systemId The DTD that is being requested
* @return The InputSource for the DTD
* @throws SAXException If anything goes wrong
*/
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;
}private InputSource getInputSource(String path, InputSource source) {
if (path != null) {
InputStream in = null;
try {
in = Resources.getResourceAsStream(path);
source = new InputSource(in);
} catch (IOException e) {
// ignore, null is ok
}
}
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这个文件。
-
用jxl读取Excel日期单元格内容
2008-09-24
if (cell.getType() == CellType.DATE) {
DateCell dateCell = (DateCell) cell;
Date date = dateCell.getDate();System.out.println(new SimpleDateFormat("yyyy/MM/dd").format(date));
} -
在Eclipse中设置自动换行
2008-04-22
-
[转]基于Converter解决Struts无法处理日期类型的问题
2008-04-06
原文地址:基于Converter解决Struts无法处理日期类型的问题
造成Struts处理日期类型数据的问题,根本原因是BeanUtils里面,只内置了对java.sql.Date的转换器,而没有对java.util.Date的转换器。使用Converter,还是需要对Apahce的BeanUtils的类库做比较多的了解,一般人图省事,就把变量类型都换成String了。
下面是DateConverter的一个例子,把它注册到ConvertUtils里面就行了:ConvertUtils.register(new DateConverter(), Date.class)
package zizz.struts;
import java.text.SimpleDateFormat;
import org.apache.commons.beanutils.Converter;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import java.text.ParseException;
/**
*
* 日期转换对象,使用该转换器,在BaseForm当中做一下注册,系统自动地帮助字符的日期表示转换为java.util.Date对象.
* @author zizz.
* Create Time:2006-9-11 19:11:47.
*/
public class DateConverter implements Converter {
/**
* 日期格式化对象.
*/
private static SimpleDateFormat df = new SimpleDateFormat();
/**
* 模式集合.
*/
private static Set<String> patterns = new HashSet<String>();
//注册一下日期的转换格式
static{
DateConverter.patterns.add("yyyy-MM-dd");
DateConverter.patterns.add("yyyy-MM-dd HH:mm");
DateConverter.patterns.add("yyyy-MM-dd HH:mm:ss");
DateConverter.patterns.add("yyyy/MM/dd HH:mm:ss");
}
/**
* 日期转换器.
* @param type Class
* @param value Object
* return Date Object.
*/
public Object convert(Class type,Object value){
if(value == null){
return null;
}else if(value instanceof String){
Object dateObj = null;
Iterator it = patterns.iterator();
while(it.hasNext()){
try{
String pattern = (String)it.next();
df.applyPattern(pattern);
dateObj = df.parse((String)value);
break;
}catch(ParseException ex){
//do iterator continue
}
}
return dateObj;
}else{
return null;
}
}
} -
关于Spring声明性事务的几种定义方式
2008-03-12
PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。对照一下,自己以前的理解确实有错误,结果就是单独定义几个没有事务的Service,调用的时候需要嵌入到其他Service方法中,认为这样才能能保证两个Service方法在一个事务里面执行。实际上声明为PROPAGATION_REQUIRED就可以了。
详细的说明请参见下面的文章:解惑Spring嵌套事务


