作者:ling091 时间:2008-12-16
我正在参与的ESB(企业服务总线)项目使用Tuscany的开源组件SDO(C++版),在使用过程中,我和其他项目组员发现了SDO存在的几个bug,这些bug我已经通过Apache网站的JIRA:
http://issues.apache.org/jira/browse/Tuscany
进行了发布,从该网站可以看到C++ SDO的所有使用者发布的bug和新特性,有些bug已经由该bug的发布者或其他用户提供了补丁。
以下是我在Apache的JIRA上发布的四个bug:
SDO的几个与序列化相关的方法不支持多线程
Windows XP
Visual C++6.0
cpp_tuscany_sdo-M3
libxml2-2.7.2
iconv-1.7.2
zlib-1.2.3
boost-1.33.1
在多线程环境中对SDO对象进行序列化和反序列化时,只有第一个线程的运行是好的,从第二个线程开始SDO对象的序列化和反序列化都失败了(多线程环境是通过开源库boost_1_33_1实现的)。这些在多线程中运行失败的方法包括:
//used when antiserializing a SDO object from a character string
const char* XSDHelperImpl::define(
const char* schema,
bool loadImportNamespace= "")
//used when serializing a SDO object into a character string
char* XMLHelperImpl::save(
DataObjectPtr dataObject,
const char* rootElementURI,
const char* rootElementName,
int indent= -1)
// used when serializing a SDO object into a xml file
void XMLHelperImpl::save(
DataObjectPtr dataObject,
const char* rootElementURI,
const char* rootElementName,
const char* xmlFile,
int indent = -1)
//used when getting a SOD object's shcema character string
char* XSDHelperImpl::generate(
const TypeList& types,
const char* targetNamespaceURI = "",
int indent = -1)
//used when getting a SDO object's schema file
void XSDHelperImpl::generateFile(
const TypeList& types,
const char* fileName,
const char* targetNamespaceURI = "",
int indent = -1)
但是与对象序列化或反序列化相关的方法中,通过文件来反序列化创建SDO对象的方法都是线程安全的:
const char* XSDHelperImpl::defineFile(
const char* schema,
bool loadImportNamespace = false)
通过比较defineFile和define方法的实现,我查找到原因是SDO涉及序列化和反序列化的方法都依赖开源的C++的xml解析器libxml2,而libxml2的一些方法并不是线程安全的,尽管网上的资料声称libxml2从2.4.7版已经做到线程安全了。通过将define方法依赖的解析函数替换为libxml2中的其他线程安全的解析函数,补丁程序的define方法已经不存在线程不安全问题。
defineFile方法依赖的libxml2中的方法是:
XMLPUBFUN int XMLCALL xmlSAXUserParseFile (
xmlSAXHandlerPtr sax,
void *user_data,
const char *filename);
对该方法的调用位于SAX2Parser类的parse_twice方法
define方法依赖的libxml2中的方法是:
XMLPUBFUN int XMLCALL xmlParseChunk (
xmlParserCtxtPtr ctxt,
const char *chunk,
int size,
int terminate);
对该方法的调用位于SAX2Parser类的stream_twice方法,在补丁程序中,将xmlParserChunk方法替换为与xmlSAXUserParseFile对应的下列方法:
XMLPUBFUN int XMLCALL xmlSAXUserParseMemory (
xmlSAXHandlerPtr sax,
void *user_data,
const char *buffer,
int size);
补丁程序只修改了SAX2Parser类的stream_twice,修改后的实现为:
void SAX2Parser::stream_twice(std::istream& input)
{
//begin 将input流读到char*
input.seekg(0, std::ios_base::end);
int size = input.tellg();
char* buffer = new char[size];
input.seekg(std::ios_base::beg);
input.read(buffer, size);
//end
parserError = false;
xmlSAXHandlerPtr handler = &SDOSAX2HandlerStruct;
//int rc = xmlSAXUserParseFile(handler, this, filename);
int rc = xmlSAXUserParseMemory(handler, this, buffer, size);
if (rc == -1)
{
sdo_error(this, "xmlSAXUserParseFile returned an error %d", rc);
SDO_THROW_EXCEPTION("parse", SDOFileNotFoundException,messageBuffer);
}
// parse twice - first was for groups
if (setter)setter->clearErrors();
//rc = xmlSAXUserParseFile(handler, this, filename);
rc = xmlSAXUserParseMemory(handler, this, buffer, size);
if (rc == -1)
{
sdo_error(this, "xmlSAXUserParseFile returned an error %d", rc);
SDO_THROW_EXCEPTION("parse", SDOFileNotFoundException,messageBuffer);
}
delete buffer;
}
对于上面提到的其他的不支持多线程的方法,由于没有在libxml2中找到与之对应的线程安全的解析函数进行替换,所以这些函数仍然不支持多线程环境。
有些反序列化方法不支持中文,或者不可用,或者中文变为乱码。
Windows XP
Visual C++6.0
cpp_tuscany_sdo-M3
libxml2-2.7.2
iconv-1.7.2
zlib-1.2.3
从一个字符串反序列化成一个SDO对象时,如果字符串中存在中文字符,则反序列化为SDO对象存在问题,不同的使用方式或者不可用、或者中文变为乱码。
下面的方法是对SDO进行封装后的从字符串反序列化为SDO对象:
DataObjectPtr generateSDOFromStr(
const char* xsdStr, const char* xmlStr,
const char* nameSpace = "")
{
DataObjectPtr root=NULL;
try
{
DataFactoryPtr mdg=DataFactory::getDataFactory();
XSDHelperPtr xsdHelper=HelperProvider::getXSDHelper(mdg);
xsdHelper->define(xsdStr);
XMLHelperPtr xmlHelper = HelperProvider::getXMLHelper(mdg);
XMLDocumentPtr xmlDoc = xmlHelper->load(xmlStr,nameSpace);
root = xmlDoc->getRootDataObject();
}
catch(SDORuntimeException e)
{
cout << "Exception in generateSDOFromStr"<< endl;
cout<< e<<endl;
}
catch(...)
{
cout<<"Exception not SDORuntimeException"<<endl;
}
return root;
}
这是对该方法的两种调用方式:
void testGenerateSDOFromStr() { //直接使用字符串 string xsdStr = "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" elementFormDefault=\"qualified\"><xs:element name=\"company\">"; xsdStr += "<xs:complexType><xs:attribute name=\"name\" type=\"xs:string\" /></xs:complexType></xs:element></xs:schema>"; string xmlStr = "<?xml version=\"1.0\" encoding=\"GB2312\"?><company name = \"中国xidian\"/>"; //--------------第一种调用方式------------- //xmlStr写为GB2312或UTF-8结果都是乱码 但是创建sdo对象都是正确的 //DataObjectPtr dob = generateSDOFromStr(xsdStr.c_str(), xmlStr.c_str() ); //--------------第二种调用方式---------- //只要第二个参数中有中文,构造的sdo对象就是null DataObjectPtr dob = generateSDOFromStr(xsdStr.c_str(), "<?xml version=\"1.0\" encoding=\"UTF-8\"?><company name = \"中国xidian\"/>"); if(dob != NULL) { cout<<dob->getCString("name")<<endl;//涓浗xidian } else { cout<<"null"<<endl; } }
2.4 解决方案:
造成乱码的原因是SDO使用的libxml2对中文的支持很差,经过libxml2解析后,SDO得到的内容已经是乱码。解决方法是可以使用iconv提供的编码转换函数进行编码转换,在遇到带中文的字符串需要显示时,将其转换为GB2312编码即可。
反序列化为SDO对象的schema文件与从该SDO对象生成的schema文件不一致。
Windows XP
Visual C++6.0
cpp_tuscany_sdo-M3
libxml2-2.7.2
iconv-1.7.2
zlib-1.2.3
从schema文件和xml文件反序列化成一个SDO对象,然后再从这个对象生成schema文件,前后两个schema文件的定义不一致,在后面的schema文件中,属性attribute变成了元素element。
下面是测试用例:
Tuscany SDO中的Bug续: http://ling091.iteye.com/admin/blogs/295580