javax.xml.parsers.DocumentBuilderFactory,javax.xml.transform.TransformerFactory 问题

 问题来源:

开发环境是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.DocumentBuilderFactory
2.如果1没有找到,则找java.home/lib/jaxp.properties 文件,如果文件存在,在文件中查找key=javax.xml.parsers.DocumentBuilderFactory
3.如果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);
  }

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值