建国's profile小白的生活PhotosBlogLists Tools Help

Blog


    April 27

    me

    ssssssssssssssss

    最近事情真多

    昨天去了趟仁济医院,到了急诊室,坐了会,心理好复杂,但是上天不能改变事实,就是这样,人死了
    看到被推出抢救室,套上蓝色的塑料袋,被推进了太平间,走了,感慨,人生好无奈!
    我努力朝向天空,试着让眼泪不流下来,不想走,想要说,但是无法喊出,突然天空一片黑,原来你已经离开我...
    April 16

    看对象引用是怎样严重影响垃圾收集器的

     如果您认为 Java 游戏开发人员是 Java 编程世界的一级方程式赛车手,那么您就会明白为什么他们会如此地重视程序的性能。

      如果您认为 Java 游戏开发人员是 Java 编程世界的一级方程式赛车手,那么您就会明白为什么他们会如此地重视程序的性能。 游戏开发人员几乎每天都要面对的性能问题,往往超过了一般程序员考虑问题的范围。哪里可以找到这些特殊的开发人员呢?Java 游戏社区就是一个好去处。 虽然在这个站点可能没有很多关于服务器端的应用,但是我们依然可以从中受益,看看这些“惜比特如金”的游戏开发人员每天所面对的,我们往往能从中得到宝贵的经验。让我们开始游戏吧!

      对象泄漏

      游戏程序员跟其他程序员一样――他们也需要理解 Java 运行时环境的一些微妙之处,比如垃圾收集。垃圾收集可能是使您感到难于理解的较难的概念之一, 因为它并不能总是毫无遗漏地解决 Java 运行时环境中堆管理的问题。似乎有很多类似这样的讨论,它的开头或结尾写着:“我的问题是关于垃圾收集”。

      假如您正面遭遇内存耗尽(out-of-memory)的错误。于是您使用检测工具想要找到问题所在,但这是徒劳的。您很容易想到另外一个比较可信的原因:这是 Java 虚拟机堆管理的问题,而不会认为这是您自己的程序的缘故。但是,正如 Java 游戏社区的资深专家不止一次地解释的,Java 虚拟机并不存在任何被证实的对象泄漏问题。实践证明,垃圾收集器一般能够精确地判断哪些对象可被收集,并且重新收回它们的内存空间给 Java 虚拟机。所以,如果您遇到了内存耗尽的错误,那么这完全可能是由您的程序造成的,也就是说您的程序中存在着“无意识的对象保留(unintentional object retention)”。

      内存泄漏与无意识的对象保留

      内存泄漏和无意识的对象保留的区别是什么呢?对于用 Java 语言编写的程序来说,确实没有区别。两者都是指在您的程序中存在一些对象引用,但实际上您并不需要引用这些对象。一个典型的例子是向一个集合中加入一些对象以便以后使用它们,但是您却忘了在使用完以后从集合中删除这些对象。因为集合可以无限制地扩大,并且从来不会变小,所以当您在集合中加入了太多的对象(或者是有很多的对象被集合中的元素所引用)时,您就会因为堆的空间被填满而导致内存耗尽的错误。垃圾收集器不能收集这些您认为已经用完的对象,因为对于垃圾收集器来说,应用程序仍然可以通过这个集合在任何时候访问这些对象,所以这些对象是不可能被当作垃圾的。

      对于没有垃圾收集的语言来说,例如 C++ ,内存泄漏和无意识的对象保留是有区别的。C++ 程序跟 Java 程序一样,可能产生无意识的对象保留。但是 C++ 程序中存在真正的内存泄漏,即应用程序无法访问一些对象以至于被这些对象使用的内存无法释放且返还给系统。令人欣慰的是,在 Java 程序中,这种内存泄漏是不可能出现的。所以,我们更喜欢用“无意识的对象保留”来表示这个令 Java 程序员抓破头皮的内存问题。这样,我们就能区别于其他使用没有垃圾收集语言的程序员。

      跟踪被保留的对象

      那么当发现了无意识的对象保留该怎么办呢?首先,需要确定哪些对象是被无意保留的,并且需要找到究竟是哪些对象在引用它们。然后必须安排好 应该在哪里释放它们。最容易的方法是使用能够对堆产生快照的检测工具来标识这些对象,比较堆的快照中对象的数目,跟踪这些对象,找到引用这些对象的对象,然后强制进行垃圾收集。有了这样一个检测器,接下来的工作相对而言就比较简单了:

      等待直到系统达到一个稳定的状态,这个状态下大多数新产生的对象都是暂时的,符合被收集的条件;这种状态一般在程序所有的初始化工作都完成了之后。

      强制进行一次垃圾收集,并且对此时的堆做一份对象快照。

      进行任何可以产生无意地保留的对象的操作。

      再强制进行一次垃圾收集,然后对系统堆中的对象做第二次对象快照。

      比较两次快照,看看哪些对象的被引用数量比第一次快照时增加了。因为您在快照之前强制进行了垃圾收集,那么剩下的对象都应该是被应用程序所引用的对象,并且通过比较两次快照我们可以准确地找出那些被程序保留的、新产生的对象。

      根据您对应用程序本身的理解,并且根据对两次快照的比较,判断出哪些对象是被无意保留的。

      跟踪这些对象的引用链,找出究竟是哪些对象在引用这些无意地保留的对象,直到您找到了那个根对象,它就是产生问题的根源。

    显式地赋空(nulling)变量

      一谈到垃圾收集这个主题,总会涉及到这样一个吸引人的讨论,即显式地赋空变量是否有助于程序的性能。赋空变量是指简单地将 null 值显式地赋值给这个变量,相对于让该变量的引用失去其作用

      清单 1. 局部作用域

      

    public static String scopingExample(String string) {

      StringBuffer sb = new StringBuffer();

      sb.append("hello ").append(string);

      sb.append(", nice to see you!");

      return sb.toString();

      }

            当该方法执行时,运行时栈保留了一个对 StringBuffer 对象的引用,这个对象是在程序的第一行产生的。在这个方法的整个执行期间,栈保存的这个对象引用将会防止该对象被当作垃圾。当这个方法执行完毕,变量 sb 也就失去了它的作用域,相应地运行时栈就会删除对该 StringBuffer 对象的引用。于是不再有对该 StringBuffer 对象的引用,现在它就可以被当作垃圾收集了。栈删除引用的操作就等于在该方法结束时将 null 值赋给变量 sb。

      错误的作用域

      既然 Java 虚拟机可以执行等价于赋空的操作,那么显式地赋空变量还有什么用呢?对于在正确的作用域中的变量来说,显式地赋空变量的确没用。但是让我们来看看另外一个版本的 scopingExample 方法,这一次我们将把变量 sb 放在一个错误的作用域中。

      清单 2. 静态作用域

      

    static StringBuffer sb = new StringBuffer();

      public static String scopingExample(String string) {

      sb = new StringBuffer();

      sb.append("hello ").append(string);

      sb.append(", nice to see you!");

      return sb.toString();

      }

            现在 sb 是一个静态变量,所以只要它所在的类还装载在 Java 虚拟机中,它也将一直存在。该方法执行一次,一个新的 StringBuffer 将被创建并且被 sb 变量引用。在这种情况下,sb 变量以前引用的 StringBuffer 对象将会死亡,成为垃圾收集的对象。也就是说,这个死亡的 StringBuffer 对象被程序保留的时间比它实际需要保留的时间长得多――如果再也没有对该 scopingExample 方法的调用,它将会永远保留下去。

    一个有问题的例子

      即使如此,显式地赋空变量能够提高性能吗?我们会发现我们很难相信一个对象会或多或少对程序的性能产生很大影响,直到我看到了一个在 Java Games 的 Sun 工程师给出的一个例子,这个例子包含了一个不幸的大型对象。

      清单 3. 仍在静态作用域中的对象

      

    private static Object bigObject;

      public static void test(int size) {

      long startTime = System.currentTimeMillis();

      long numObjects = 0;

      while (true) {

      //bigObject = null; //explicit nulling

      //SizableObject could simply be a large array, e.g. byte[]

      //In the JavaGaming discussion it was a BufferedImage

      bigObject = new SizableObject(size);

      long endTime = System.currentTimeMillis();

      ++numObjects;

      // We print stats for every two seconds

      if (endTime - startTime >= 2000) {

      System.out.println("Objects created per 2 seconds = " + numObjects);

      startTime = endTime;

      numObjects = 0;

      }

      }

      }

            这个例子有个简单的循环,创建一个大型对象并且将它赋给同一个变量,每隔两秒钟报告一次所创建的对象个数。现在的 Java 虚拟机采用 generational 垃圾收集机制,新的对象创建之后放在一个内存空间(取名 Eden)内,然后将那些在第一次垃圾收集以后仍然保留的对象转移到另外一个内存空间。在 Eden,即创建新对象时所在的新一代空间中,收集对象要比在“老一代”空间中快得多。但是如果 Eden 空间已经满了,没有空间可供分配,那么就必须把 Eden 中的对象转移到老一代空间中,腾出空间来给新创建的对象。如果没有显式地赋空变量,而且所创建的对象足够大,那么 Eden 就会填满,并且垃圾收集器就不能收集当前所引用的这个大型对象。所产生的后果是,这个大型对象被转移到“老一代空间”,并且要花更多的时间来收集它。

    通过显式地赋空变量,Eden 就能在新对象创建之前获得自由空间,这样垃圾收集就会更快。实际上,在显式赋空的情况下,该循环在两秒钟内创建的对象个数是没有显式赋空时的5倍――但是仅当您选择创建的对象要足够大而可以填满 Eden 时才是如此, 在 Windows 环境、Java虚拟机 1.4 的默认配置下大概需要 500KB。那就是一行赋空操作产生的 5 倍的性能差距。但是请注意这个性能差别产生的原因是变量的作用域不正确,这正是赋空操作发挥作用的地方,并且是因为所创建的对象非常大。

      最佳实践

      这是一个有趣的例子,但是值得强调的是,最佳实践是正确地设置变量的作用域,而不要显式地赋空它们。虽然显式赋空变量一般应该没有影响,但总有一些反面的例子证明这样做会对性能产生巨大的负面影响。例如,迭代地或者递归地赋空集合内的元素使得这些集合中的对象能够满足垃圾收集的条件,实际上是增加了系统的开销而不是帮助垃圾收集。请记住这是个有意弄错作用域的例子,其实质是一个无意识的对象保留的例子。

    April 09

    创业假象

    上海的创业公司,本人领衔,方向为电子商务。启动资金雄厚,目标也不邪门。这个生意的道理很简单:帮助别人,我们同时从中受益。

    我们想成为一家以技术为成功增添筹码的电子商务企业;想做出一个产品体验良好且颇有亮点的网站;想做一件对人们有益让人们的生活变得更加美好的事情。

    再三权衡,网站平台将采用J2EE分布式架构。

    目前技术部一个人都没有,诚恳地欢迎在上海的程序员朋友加盟。拟招聘人数:5-8名。

    技术总监1名。薪水8000-10000元。要求是J2EE+Oracle+Linux等方面的超高手,有Web 2.0开发经验。有过大中型项目开发管理经验,做事仔细认真负责。

    java熟练程序员2-3名。薪水5000元左右。要求参与过大中型项目的开发,能独自完成既定模块的开发工作,编程习惯良好。懂Linux服务器维护者优先考虑。

    初级java程序员:2-4名。薪水3000元左右。要求做过java开发。不熟练不要紧,咱们可以一起成长。
    (注:以上所说薪水均为人民币,非美金,亦非越南盾。)

    补充要求:年龄不要比我老(我1982) ,学历不要比我高(我大本)。尤其欢迎有梦想敢拼搏的年轻朋友。

    办公地点在上海,张江科技园。8月份开工。公司目前还没注册,预计下月办完注册手续。

    能不能成为同事,那要靠缘分;不妨先认识一下,交个朋友。我深知很多朋友对于创业型公司的警惕;当然也了解很多朋友对于创业公司的热爱。加入一个创业公司,风险与机遇并存。有可能,大家一起做出了傲人业绩;当然也有可能,我们最终黯然神伤。但可以肯定的一点就是:您的表现对这家公司的未来至关重要;您可以选择让这家公司成功,或者让这家公司失败。因为我们是一起创业的团队。

    我不拿股份期权忽悠人。创业公司说这个太遥远,甚至有点扯淡。但是,当我们一起做得开心并有成就感的时候,请相信我会跟每一位同仁仔细商谈这件事情。而在此之前,每月按时给您付薪水,是我的责任所在。

     

    April 07

    在WEBLOGIC SERVER 10中使用JAX-WS和JAXB

    摘要

      JAX-WS(Java Architecture for Web Services)是JAX-RPC的后续版本。它是一种基于标准的API,可用于编写、汇编和部署Java Web services。JAXB(Java Architecture for XML Binding)是一种Java/XML绑定技术。JAX-WS将使用JAXB处理所有的Java绑定操作。

      本文将简要介绍BEA WebLogic Server 10.1中所支持的JAX-WS 2.0和JAXB 2.0。读者可通过文章中的示例代码入门。

    JAXB 2.0 Java开发人员快速指南

    JAX-WS使用JAXB处理所有的Java绑定操作,因此本文将重点讨论与JAX-WS有关的JAXB。熟练的Java开发人员通常都很繁忙。考虑到这一点,我将主要讨论以下两方面内容:

    目前使用JAXB 2.0可以完成的任务。
    目前使用JAXB 2.0无法完成的任务。

    如果您忙得连阅读这篇文章的时间都没有,请直接 下载 本文所提供的示例代码。压缩包中的README文件介绍了具体的操作步骤。


    使用JAXB 2.0可以完成那些任务?

    下面列出了使用JAXB 2.0可以实现的一些比较有趣的任务。我的意思并不是说使用其他Java-to-XML/XML-to-Java绑定技术就不能实现这些操作。我只是列出了使用JAXB 2.0可以完成的操作:

    通过含有一个或多个<xs:schema>元素的WSDL生成Java对象图。这些<xs:schema>元素可以使用<xs:import>和<xs:include>元素引用其他的<xs:schema>元素。
    通过Java对象图生成XML Schema文档。
    利用快速信息集分析程序(SAX和StAX)和串行器(serializer)。
    随机访问XML文档的XML信息集。
    直接在XML Schema文件或外部绑定自定义文件中嵌入绑定声明。
    在取消编组(unmarshalling)时使用基于事件的流模型。
    编组二进制数据(比如说,处理MTOM和MIME附件)。
    开发自己的插件,扩展JAXB代码生成功能。然后,这些插件(作为类封装在一个.jar文件中)可以访问JAXB生成的代码,还可生成另外的类/方法/字段/注释/评论。
    编写定制代码将已有类转化为由JAXB模式编译程序生成的类。

    使用JAXB 2.0无法完成的操作

    下面列出了使用JAXB 2.0无法完成的操作(或者说是我不知道如何实现):

    通过XML文档生成XML模式。实际上,这并没有什么大不了的。因为其他工具(如Stylus Studio、XMLSpy和XMLBuddy Pro)可以实现这一功能。
    匹配StAX或SAX解析的性能指数。解析同一XML文档时,SAX需要10ms,StAX需要46ms,而JAXB 2.0需要59ms。
    使用JAXB进行Java绑定的示例POJO JAX-WS Web服务
    JAX-WS是一种基于标准的API,可用于编写、汇编和部署Java Web services。它使用JAXB处理所有与此相关的Java绑定操作。JAX-WS 2.0/2.1并不支持JAX-RPC或Apache Beehive XMLBean类型的使用,只支持JAXB类型的XMLBean。

    JAX-WS提供了两个编程模型,用于开发Web服务端点。

    1.从Java开始(Start from Java)——此编程模型使您能够充分地控制Web服务端的方法签名(method signature)中所使用的Java数据类型。在该模型中,您可以手动编写(或者使用工具生成)Java对象,这些Java对象将作为Web服务操作及JWS注释的输入参数和返回值使用。

    BEA为“从Java开始”编程模型提供了jwsc Ant任务。它将Glassfish wsimport Ant任务封装在内部(即从内部调用)。因此我们不用直接在build.xml文件中使用Ant任务。

    Ant Task Reference for jwsc 这篇BEA文档介绍了如何使用<jws>元素的type="JAXWS"属性生成JAXB工件。jwsc Ant任务中的<binding>子元素可用于指定所要使用的JAXB绑定自定义文件。

    2.从WSDL开始(Start from WSDL)——此编程框架将通过WSDL的内容生成Web服务端点的主干代码。WSDL中<xs:schema>部分生成的Java数据类型将作为Web服务操作的输入参数和返回值使用。

    BEA为“从WSDL开始”编程模型提供了wsdlc Ant任务。它将Glassfish wsimport Ant任务封装在内部。因此我们不用直接在build.xml文件中使用Ant任务。

    Ant Task Reference for wsdlc 这篇BEA文档介绍了如何使用<wsdlc>元素的type="JAXWS"属性生成JAXB工件。wsdlc Ant任务中的<binding>子元素可用于指定所要使用的JAXB绑定自定义文件。

    文章的其余部分将介绍如何创建、部署和测试基于POJO(Plain-Old Java Object)的JAX-WS Web服务端点,即DataStagingService。

    创建定制文件

    第一步需要创建一个JAX-WS定制文件。该文件还可以充当JAXB绑定定制文件,并允许我们控制JAX-WS和JAXB构建时流程,以及它们生成的工件。

    定制文件是一个XML文档,它符合XML模式的http://java.sun.com/xml/ns/jaxws和http://java.sun.com/xml/ns/jaxb名称空间。有关创建该定制文件的详细信息,请参阅 JAX-WS 2.0 Beta Customizations。

    DataStagingService Web服务的JAX-WS定制文件相当小,因此我列出了该文件的内容:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <bindings
    xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    xmlns="http://java.sun.com/xml/ns/jaxws"
    wsdlLocation="DataStagingService2.wsdl"

    <bindings
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    node="wsdl:definitions"

    <package name="services.datastaging">
    <jxb:javadoc>
    <![CDATA[<body>Package level documentation for generated
    package services.datastaging.</body>]]>
    </jxb:javadoc>
    </package>
    <jxb:schemaBindings>
    <jxb:package name="com.acmeworld.irad.services.datastaging"/>
    </jxb:schemaBindings>
    </bindings>
    </bindings>

    我将主要使用该定制文件控制生成类的Java包名。您可以在该定制文件中实现更多其他功能,请参阅 JAX-WS 2.0 Beta Customizations。

    下面的清单展示了DataStagingService Web服务的WSDL中所使用的XML Schema:

    <xs:schema
    targetNamespace="http://services.irad.acmeworld.com/datastaging"
    xmlns:tns="http://services.irad.acmeworld.com/datastaging"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified"

    <xs:complexType name="DataStaging">
    <xs:sequence>
    <xs:element name="inputURIs">
    <xs:complexType>
    <xs:sequence>
    <xs:element name="inputURI" maxOccurs="unbounded">
    <xs:complexType>
    <xs:sequence>
    <xs:element name="uri" type="xs:anyURI"/>
    </xs:sequence>
    </xs:complexType>
    </xs:element>
    </xs:sequence>
    </xs:complexType>
    </xs:element>
    </xs:sequence>
    </xs:complexType>

    <xs:complexType name="DataStagingResponse">
    <xs:sequence>
    <xs:element name="outputURIs">
    <xs:complexType>
    <xs:sequence>
    <xs:element name="outputURI" maxOccurs="unbounded">
    <xs:complexType>
    <xs:sequence>
    <xs:element name="uri" type="xs:anyURI"/>
    </xs:sequence>
    </xs:complexType>
    </xs:element>
    </xs:sequence>
    </xs:complexType>
    </xs:element>
    </xs:sequence>
    </xs:complexType>

    <xs:element name="dataStaging" type="tns:DataStaging"/>
    <xs:element name="dataStagingResponse" type="tns:DataStagingResponse"/>
    </xs:schema>

    此WSDL的XML Schema部分中的内容相当普通。请求和响应消息是由普通的(具有匿名和显式内容模型的)全局complexType元素一起创建的。DataStagingService是一个doc/literal样式的Web服务,因此其中含有全局元素与WSDL的<part>元素结合使用。

    使用build.xml文件构建
    我将使用WebLogic Server 10中附带的Eclipse IDE继续演示。WebLogic for Workshop 10中并没有特定的Eclipse IDE插件可用于JAX-WS和JAXB开发,因此我将使用build.xml Ant脚本和Ant View窗口。

    这个build.xml文件的内容太长,因此我只列出了其中的重要部分。

    <path id="jaxws.classpath">
    <fileset dir="${bea.home}/modules">
    <include name="javax.xml.stream_1.0.0.0.jar"/>
    <include name="javax.jws_2.0.jar"/>
    <include name="javax.xml.bind_2.0.jar"/>
    <include name="javax.xml.ws_2.0.jar"/>
    <include name="javax.xml.soap_1.3.0.0.jar"/>
    <include name="javax.activation_1.1.jar"/>
    <include name="glassfish.jaxws.rt_2.0.1.jar"/>
    <include name="glassfish.jaxb_2.0.5.jar"/>
    <include name="glassfish.jaxws.saaj.impl_2.0.1.jar"/>
    <include name="glassfish.jaxws.tools_2.0.1.jar"/>
    </fileset>
    </path>

    上面的清单指出了build.xml文件中所使用的<path>元素。它表示WebLogic Server 10将在其JAX-WS和JAXB实现中使用共享的Java EE库模块来实现类。如您所见,这些类来自Glassfish RI JAR,而并非由WebLogic Server 10 Web Services栈开发小组编写。共享的Java EE库模块有利于Glassfish JAR的封装,并允许将它们的单个副本用于各种内部(对于BEA)或外部用途。众所周知,这些库模块位于${BEA_HOME}/modules目录。

    上述清单中的JAR位于用户定义的库中。这样更易于编写在Eclipse IDE内部使用JAX-WS和JAXB API的代码。

    生成JAX-WS服务端点和JAXB类

    接下来,我们将实现DataStagingService Web服务的代码。首先,运行BEA wsdlc Ant任务,目的是生成主干JAX-WS端点实现和JAXB类。

    WebLogic Server 10.1将使用Glassfish Project项目中的jar文件用于其JAX-WS和JAXB实现。type="JAXWS"属性将指定使用JAX-WS和JAXB,而不是JAX-RPC。

    <target name="run-wsdlc" depends="clean">
    <taskdef name="wsdlc" classname="weblogic.wsee.tools.anttasks.WsdlcTask"
    classpathref="compile.classpath" />

    <property name="client.binding" value="custom-client.xjb"/>

    <wsdlc type="JAXWS"
    srcWsdl="etc/${wsdl.file.name}.wsdl"
    destJwsDir="WebContent/WEB-INF/lib"
    destImplDir="${src.dir}"
    explode="false"
    verbose="${verbose}"
    debug="${debug}"
    failonerror="true">
    <binding dir="etc" includes="${client.binding}"/>
    <classpath>
    <path refid="compile.classpath"/>
    </classpath>
    </wsdlc>
    ; /target>

    type="JAXWS"属性和<binding>子元素需要格外注意。

    这时,我们应该能够运行Ant构建文件了。结果将生成JAX-WS端点实现的代码。由于我提供了一个定制文件,因此生成代码将位于com.acmeworld.irad.services.datastaging包中。JAX-WS端点实现的完整代码比较长,因此我只摘录了其中的一部分:

    public com.acmeworld.irad.services.datastaging.DataStagingResponse.OutputURIs dataStaging(com.acmeworld.irad.services.datastaging.DataStaging.InputURIs inputURIs)
    {
    DataStagingResponse dataStagingResponse = null;
    InputStream inputstream = null;

    try
    {
    //DataStaging.InputURIs contains zero or more
    //DataStaging.InputURIs.InputURI JAXB objects.
    //We loop through them, and use one of their getter
    //methods to print out a bound value.

    DataStaging.InputURIs.InputURI inputURI = null;
    List inputURIList = inputURIs.getInputURI();

    for (int i = 0; i < inputURIList.size(); i++)
    {
    inputURI = inputURIList.get(i);
    log("dataStaging(InputURIs)", "inputURI.getUri()=" + inputURI.getUri());
    }

    //Next, we show one way to use the JAXB API, to convert
    //the DataStaging.InputURIs input parameter to a byte[].
    //This byte[] will contain an XML representation of that
    //input parameter.

    JAXBContext jc = JAXBContext.newInstance(DataStaging.InputURIs.class);
    Marshaller marshaller = jc.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    JAXBElement je = new JAXBElement(
    new QName("http://services.irad.acmeworld.com/datastaging","inputURIs"),
    DataStaging.InputURIs.class,
    inputURIs
    );
    marshaller.marshal(je, baos);

    //We use an existing XML file for the response from the
    //Web service operation. We’ll load this XML file from
    //the WEB-INF/classes directory, and use the JAXB API
    //to create the JAXB object of our response.
    inputstream = Thread.currentThread().getContextClassLoader().getResourceAsStream("SampleDataStagingResponseDocument.xml");

    jc = JAXBContext.newInstance(DataStagingResponse.class);
    Unmarshaller unmarshaller = jc.createUnmarshaller();
    dataStagingResponse = (DataStagingResponse)unmarshaller.unmarshal(inputstream);
    }
    catch (Exception e)
    {
    throw new RuntimeException(e);
    }
    finally
    {
    if (inputstream != null) try {} catch (Exception e){}
    }

    return dataStagingResponse.getOutputURIs();
    }

    其中的代码注释很好地解释了其过程,因此我在此处就不再赘述了。

    编译JAX-WS服务端点

    BEA jwsc Ant任务用于编译JAX-WS服务端点并生成待部署的WAR文件。

    <target name="run-jwsc">
    <taskdef name="jwsc" classname="weblogic.wsee.tools.anttasks.JwscTask"
    classpathref="compile.classpath" />

    <jwsc
    destdir="${domain.home}/deployments/${project.name}"
    srcdir="${src.dir}/${package.path}"
    classpath="WebContent/WEB-INF/lib/${wsdl.file.name}_wsdl.jar"
    keepGenerated="${keep}"

    <binding dir="etc" includes="${client.binding}"/>
    <module explode="false" name="${portType.name}Impl">
    <jws
    type="JAXWS"
    file="${service.name}Impl.java"
    compiledWsdl="WebContent/WEB-INF/lib/${wsdl.file.name}_wsdl.jar"

    <WLHttpTransport
    contextPath="datastaging"
    serviceUri="DataStagingService"
    portName="DataStagingServicePort"
    />
    </jws>
    </module>
    <classpath>
    <path refid="compile.classpath"/>
    <pathelement location="WebContent/WEB-INF/lib/${wsdl.file.name}_wsdl.jar"/>
    </classpath>
    </jwsc>
    </target>

    同样,此处的type="JAXWS"属性和<binding>子元素值得格外注意。该代码还演示了如何避免将特定于WebLogic注释(比如说@WLHttpTransport)放在JWS中。

    使用build.xml文件部署

    JAX-WS中有一个倍爱好评的特性,即部署描述符的使用是可选的。这种特性很好,因为它解决了多个供应商所提供的JAX-WS实现之间的可移植性问题。

    基于POJO的JAX-WS Web services已封装为Java EE Web应用程序,并且WebLogic Server 10所提供的wsdeploy Ant任务可以将这些应用程序部署到运行中的WLS实例上。此处,我将在build.xml文件中使用该wsdeploy Ant任务。

    <target name="deploy">
    <property name="wls.username" value="weblogic"/>
    <property name="wls.password" value="weblogic"/>
    <property name="wls.hostname" value="localhost"/>
    <property name="wls.port" value="7031"/>
    <property name="wls.server.name" value="W4WPAdminServer"/>

    <taskdef name="wldeploy"
    classname="weblogic.ant.taskdefs.management.WLDeploy"
    classpathref="compile.classpath" />

    <wldeploy
    action="deploy"
    name="${project.name}"
    source="${domain.home}/deployments/${project.name}"
    user="${wls.username}"
    password="${wls.password}"
    verbose="true"
    adminurl="t3://${wls.hostname}:${wls.port}"
    targets="${wls.server.name}"
    />
    </target>

    使用WebLogic Test Client进行测试

    WebLogic Test Client是一个Java EE Web应用程序。和WebLogic Server Administration Console (console.war)一样,它自动部署于您的WebLogic Server 10.1实例中。该应用程序的URL为:

    http://<host>:<port>/wls_utc

    DataStagingService的WSDL的URL为:

    http://<host>:<port>/datastaging/DataStagingService?WSDL

    输入该WSDL URL之后,单击“Test”按钮,然后便可在接下来出现的页面中输入测试请求XML。下面是一个示例请求XML:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <dataStaging xmlns="http://services.irad.acmeworld.com/datastaging">
    <inputURIs>
    <inputURI>
    <uri>http://www.altova.com</uri>
    </inputURI>
    <inputURI>
    <uri>http://www.amazon.com</uri>
    </inputURI>
    </inputURIs>
    </dataStaging>

    WebLogic Test Client相当优秀,但是它实际上并不能测试在客户端上使用JAXB和JAX-WS。最后一步将实现这一任务。

    使用JAX-WS Web服务客户端进行测试

    最后,我们将使用JAX-WS Web服务客户端调用DataStagingService Web服务。最后一部分的代码要比JAX-WS服务端点的代码稍微复杂一些,因此它需要使用JAXB API。

    try
    {
    DataStagingService service = new DataStagingService(
    new URL("http://localhost:7031/datastaging/DataStagingService?WSDL"),
    new QName("http://services.irad.acmeworld.com/datastaging",
    _properties.getProperty("datastaging.service.portName"))
    );

    Dispatch sourceDispatch = service.createDispatch(
    new QName("http://services.irad.acmeworld.com/datastaging",
    "DataStagingService"),
    Source.class,
    Service.Mode.PAYLOAD
    );

    InputStream inputstream = Thread.currentThread().getContextClassLoader().
    getResourceAsStream("SampleDataStagingRequestDocument.xml");

    Source responseSource = sourceDispatch.invoke(new StreamSource(inputstream));

    javax.xml.transform.TransformerFactory factory =
    javax.xml.transform.TransformerFactory.newInstance();

    javax.xml.transform.Transformer transformer = factory.newTransformer();
    transformer.setOutputProperty(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
    transformer.setOutputProperty(javax.xml.transform.OutputKeys.METHOD, "xml");

    javax.xml.transform.stream.StreamResult streamResult =
    new javax.xml.transform.stream.StreamResult();

    streamResult.setOutputStream(new java.io.ByteArrayOutputStream());
    transformer.transform(responseSource, streamResult);

    System.out.println((new StringBuffer()).append("response=").
    append(streamResult.getOutputStream()).toString());
    }
    catch(Throwable throwable)
    {
    throwable.printStackTrace();
    System.out.println((new StringBuffer()).append("Unexpected exception: ").
    append(throwable.getMessage()).toString());
    }

    其中大多数代码专门用于执行XSL转换,并且使用了JDK中的JAXP API类。首先,创建一个DataStagingService服务桩(stub)。该服务桩将接收一个URL对象(指向服务端点的WSDL)和一些与XML相关的内容,用于建立要调用的服务。后者将在检索WSDL之后使用。

    注意,DataStagingService服务桩和相关类是由BEA clientgen Ant任务生成的。我并没展示这些代码,不过该Ant任务也拥有一个type="JAXWS"和一个<binding>子元素。

    摘录代码的其余部分包括:

    创建Dispatch对象,该对象用于调用Web服务操作。Dispatch对象在功能上等同于JAX-RPC中的Call对象(与DII编程模型一起使用)。
    通过XML文档创建有效负载。这违背了“为各个元素实例化一个对象”的实践,JAX-RPC需要这样。
    调用Web服务操作。此外需要格外小心,因为“操作名称”包装器元素并不会自动添加。如果服务端点需要一个,则需要自己添加。
    使用JAXP转换类将响应从Web服务操作编组到java.io.ByteArrayOutputStream中。您可以处理所需的一切内容(比如说byte[]、String和XML)。同样,这远没有在JAX-RPC中处理JavaBean图(或javax.xml.soap.SOAPElement对象)费力。
    以上是整个步骤的总结。希望您可更好的理解如何在WebLogic Server 10中使用JAX-WS 2.0和JAXB 2.0实现。这些实现中的API即可以在服务提供者端使用,也可以在服务用户端使用。请查看我所提供的示例代码,您会发现其中没有使用(或导入)任何特定于WebLogic的类。这表示该代码是完全可移植的,并且应该能够在Axis2上编译,而无需对代码(或定制文件)进行任何修改。但是,您需要修改build.xml文件,使JAX-WS和JAXB实现使用Axis2所使用的.jar文件。还需要修改用于JAX-WS、JAXB和部署的Ant任务。

    一些最佳实践

    以下列出了在WebLogic Server 10中使用JAX-WS和JAXB 实现的一些最佳实践:

    避免在代码中使用特定于供应商的注释。这样在尝试不同供应商的JAX-WS实现时就无需对代码进行修改。通常,JAX-WS实现供应商都提供了在Ant任务中访问特定于供应商注释的方法。WebLogic Server使用的是这一选项,因此您应该利用这一特性,以避免在JWS中使用特定于供应商的注释。
    在可能的地方使用JAXP StreamSource和 and StreamResult。javax.xml.transform.stream.StreamSource和javax.xml.transform.stream.StreamResult所类提供的方法可以最有效地处理Java串行化(和并行化)问题。因此,您应该尽可能地使用它们。
    缓存JAXBContext对象。javax.xml.bind.JAXBContext对象是一个用于通过Java类创建JAXB对象的工厂。创建JAXBContext对象需要的开销比较大,因此应该缓存它们从而便于重用。
    使用JAXBElement编组内部类。JAXB 2.0将使用内部类用于匿名complexType元素。如果您之后在Web服务操作的方法签名中使用其中某个元素,那么将在构建时接收到一个javax.xml.bind.MarshalException异常。问题是内部类并不是高级元素,因此并没有@XmlRootElement注释。解决的方法是为内部类创建一个JAXBElement对象。要了解如何创建该对象,请在DataStagingServiceImpl.java文件中搜索"JAXBElement"。
    下载
    wls10wss_jaxws-article1.zip——该zip文件含有本教程所使用的所有源代码。
    结束语
    JAX-WS和JAXB是用于构建下一代基于Java的Web服务的最有前途的两个API。如今,WebLogic Server 10 Web Services栈通过Glassfish JAR和BEA Ant任务这两个API都提供了支持。

    您可以使用WebLogic Server 10 Web Services栈为任何JAX-WS实现编写、构建和部署JAX-WS Web服务,而不仅限于WebLogic Server 10中的实现。Jwsc和wsdlc Ant任务已经经过修改,现在可允许您指定生成JAX-WS和JAXB工件时所使用的定制文件。可以使用<binding>子元素实现这一目的。Jwsc和wsdlc Ant任务将从内部调用Sun的wsimport Ant任务。

    希望您可以通过本文了解使用WebLogic Server 10 Web Services栈生成JAX-WS和JAXB工件是多么地容易。但是,创建定制文件却并非易事。我们可以通过定制文件影响工件的生成流程。不过也不用担心,我将在后续的几篇文章中介绍如何创建定制文件。

    原文出处:http://dev2dev.bea.com/pub/a/2007/09/jax-ws-jaxb.html

    作者简介

      Mike Wooten 是BEA享有声望的技术解决方案小组(Technical Solutions Group,TSG)的一名高级首席解决方案工程师。他的工作时间都在帮助将BEA客户的抽象技术概念转变为能创造价值的解决方案。这些解决方案所提供的功能往往令其竞争者们望尘莫及。