问题来源:
开发环境是winxp,可以xml可正常创建和解析,但移植到linux下后,创建和解析失败。在网上查了下资料,主要有以下两篇:
参考原文地址一:http://blog.csdn.net/elseif/archive/2005/04/29/367506.aspx
参考原文一内容如下:
问题:
写了一个工具类,其中一个方法。目的是从XML字符串得到一个Document对象。 public static Document getDocumentByXMLString(String xmlString){ try{ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = null; builder = factory.newDocumentBuilder(); Document doc = null; doc = builder.parse( new InputSource( new StringReader( xmlString ) ) ); doc.normalize(); // 删除非XML数据 return doc; } catch(Exception e){ e.printStackTrace(); return null; } }
在该类的main方法中测试此方法能成功产生Document对象。可我在一个sevelet中调用此方法,死活就是产生一个null!
===============================================================
解决:
首先感谢 ruby_cn(__ http://www.ruby-cn.org/ __)的blog!按你说的,我删除了项目中和xml相关的所有包,一切如故。我干脆删了所有的包,除了jre和j2ee,还是如故。我将DocumentBuilderFactory对象打印出:System.out.println(factory);令人惊讶的结果出现了:org.apache.xerces.jaxp.DocumentBuilderFactoryImpl@1ad086a。我本来也猜测一定是某个第三方的包的类继承了javax.xml.parsers.DocumentBuilderFactory,发现javax.xml.parsers.DocumentBuilderFactory是一个抽象类,所以的确很有可能,因为抽象类是不能产生对象的。但是也留下一个疑问,程序里并没有产生继承了javax.xml.parsers.DocumentBuilderFactory的类的对象,运行时怎么会自动产生它的子类对象。难道是sevlet容器干的好事?因为抽象类不能产生对象,自动产生继承这个抽象类的对象?类似于EJB容器产生实现home、remote接口的对象?可是现在我把第三方的包全部删除了,怎么还会输出“apache”这个字眼?这时回想起用main方法不是一切正常的吗?于是跑了一下main,又是令人惊讶的结果:org.apache.crimson.jaxp.DocumentBuilderFactoryImpl@15601ea还是“apache”的实现,但是不同的类,这个类能完好的工作。不用说,一应定是jre或j2ee里有apache的东西。结果发现在jre/lib/rt.jar里有相当多的apache的包。这个类也在这里。我才刚刚知道原来jre里不全是sun的东西。现在的问题是为什么到了sevlet就会产生org.apache.xerces.jaxp.DocumentBuilderFactoryImpl的对象。我找org.apache.xerces.jaxp.DocumentBuilderFactoryImpl类,找了很久发现他在xercesImpl包里。这个包在jboss-4.0.1sp1/lib/endorsed目录下。我把xercesImpl包倒入了那个工具类所在的项目。运行main方法,果然,输出了org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。看来很清楚了,和sevlet容器没关系,只要这两个类被包含在同一个项目里,就会优先产生org.apache.xerces.jaxp.DocumentBuilderFactoryImpl对象。这是怎么回事呢,我猜想一定在DocumentBuilderFactory的产生对象的方法有个选择逻辑。这时想到jdk是有源代码的,哈哈,太好了!察看DocumentBuilderFactoryImpl的newInstance()方法:return (DocumentBuilderFactory) FactoryFinder.find( /* The default property name according to the JAXP spec */ "javax.xml.parsers.DocumentBuilderFactory", /* The fallback implementation class name */ "org.apache.crimson.jaxp.DocumentBuilderFactoryImpl");他调用了FactoryFinder.find()方法。注意在这里已经看到了"org.apache.crimson.jaxp.DocumentBuilderFactoryImpl"是的,字面意思这是作为一个后备的选择,也就是找不到其他实现了javax.xml.parsers.DocumentBuilderFactory的类,那么就返回org.apache.crimson.jaxp.DocumentBuilderFactoryImpl的对象。赶紧去看FactoryFinder.find()方法:三段注释揭露了一切:// Use the system property first// try to read from $java.home/lib/jaxp.properties// try to find services in CLASSPATH最后都找不到当然是返回后备的org.apache.crimson.jaxp.DocumentBuilderFactoryImpl啦。String serviceId = "META-INF/services/" + factoryId;打开xercesImpl包看看,果然有/META-INF/services目录。里面果然有个javax.xml.parsers.DocumentBuilderFactory文件。打开一看文件内容果然是org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。看来疑团都揭开啦。但是还有一点sevlet运行的classpath怎么会有endorsed/。打开run.bat看见:"%JAVA%" %JAVA_OPTS% "-Djava.endorsed.dirs=%JBOSS_ENDORSED_DIRS%" -classpath "%JBOSS_CLASSPATH%" org.jboss.Main % 看来endorsed是作为了一个java参数。好了,一切都明白了。现在要怎么解决呢。删除xercesImpl包是一定可以的但是关于endorsed我也不懂,好像websvice要用到。关于endorsed:http://java.sun.com/j2se/1.4.2/docs/guide/standards/ 所以我不敢删。想到的办法只有System.setProperty( "javax.xml.parsers.DocumentBuilderFactory","org.apache.crimson.jaxp.DocumentBuilderFactoryImpl" );因为// Use the system property first。好了,用完以后赶快System.setProperty( "javax.xml.parsers.DocumentBuilderFactory","org.apache.xerces.jaxp.DocumentBuilderFactoryImpl" );因为不知道jboss的那些类会用到org.apache.xerces.jaxp.DocumentBuilderFactoryImpl,所以还是要恢复过来的。好了,问题虽然解决了,但是感受到了不优雅的java。
参考原文二地址:http://www.blogjava.net/super/archive/2009/01/06/250083.html
参考原文二内容如下:
DocumentBuilderFactory.newInstance()查找DocumentBuilderFactory实现类的过程
1.在系统环境变量中(System.getProperties())中查找key=javax.xml.parsers.DocumentBuilderFactory2.如果1没有找到,则找java.home/lib/jaxp.properties 文件,如果文件存在,在文件中查找key=javax.xml.parsers.DocumentBuilderFactory3.如果2没有找到,则在classpath中的所有的jar包中查找META-INF/services/javax.xml.parsers.DocumentBuilderFactory 文件 全都没找到,则返回null
解决办法总结:
要点:通过System.setProperty指定实现类 ,通过System.getProperty获取系统中原有的值并临时保存,并在finally块中进行还原,从而避免对其他程序的影响。以下为一个完整方法,供参考,红色和蓝色部分是要点。
public static int postTaskToServer(ServerConfig server,String script, int taskTimeout, int priority, Date scheduleTime, int postTimeout) throws PosTaskToServerException{
Document document = null; String e1=System.getProperty("javax.xml.parsers.DocumentBuilderFactory"); String e2=System.getProperty("javax.xml.transform.TransformerFactory"); try { System.setProperty("javax.xml.parsers.DocumentBuilderFactory","com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"); System.setProperty("javax.xml.transform.TransformerFactory","com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); document = builder.newDocument(); Element command = document.createElement("command"); document.appendChild(command); Node node = document.createElement("cmd"); node.setTextContent("CreateTask"); command.appendChild(node); Node nodeParams = document.createElement("params"); Node nodeScript = document.createElement("script"); CDATASection cdata = document.createCDATASection(script); nodeScript.appendChild(cdata); nodeParams.appendChild(nodeScript); Node nodeTimeout = document.createElement("timeout"); nodeTimeout.setTextContent(String.valueOf(taskTimeout)); nodeParams.appendChild(nodeTimeout); Node nodePriority = document.createElement("priority"); nodePriority.setTextContent(String.valueOf(priority)); nodeParams.appendChild(nodePriority); Node nodeScheduleTime = document.createElement("starttime"); nodeScheduleTime.setTextContent(DateUtil.dateToString(scheduleTime,"yyyy-MM-dd hh:mm:ss")); nodeParams.appendChild(nodeScheduleTime); command.appendChild(nodeParams); Source xmlSource = new DOMSource(document); TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.setOutputProperty("indent", "yes"); StringWriter sr = new StringWriter(); Result result = new StreamResult(sr); transformer.transform(xmlSource, result); System.out.println(sr); String url = "http://" + server.getServerAddress() + "/AppAPI.asmx/Command"; HttpConnector http = new HttpConnector(); http.timeout = postTimeout; String re = http.doPost(url, "xml=" + sr.toString().replace("/r/n", "").replace("/n", ""), "utf-8"); InputStream is = new ByteArrayInputStream(re.getBytes()); document = builder.parse(is); Node n = document.getFirstChild(); String taskid = n.getTextContent(); return Integer.valueOf(taskid.trim()); } catch (Exception ex) { ex.printStackTrace(); return 0; }finally{ if(e1!=null) System.setProperty("javax.xml.parsers.DocumentBuilderFactory",e1); if(e2!=null) System.setProperty("javax.xml.transform.TransformerFactory",e2); }