`
liyiye
  • 浏览: 415837 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Java SE 6 Web Service 之旅

    博客分类:
  • java
阅读更多

在过去的几个月里,Sun公司已经为Java 平台的第6个版本(Java Platform Standard Edition 6)提供了二进制安装文件,Java文档和Java源代码,就是知名的“野马”。而且这飞驰的野马要拉住你这车并不算晚。

    一个显而易见的问题是,“为什么我应该关心?”对于这些质疑者,Java SE 6提升的性能包括扩平台性,从开放的程序管理到Java编译器,到系统底层和屏幕组件,到在你的源代码中混合脚本语言(支持JavaScript),到 Swing的整洁的外观,到XML数字签名,到智能卡I/O API,到JMX监听线程的升级,为Web服务的服务提供者使用Annotations注释和更加简单的客户端管理——在我们要关心的时候,这些名字仅仅 是这个平台中的一小部分新功能。(java.net站点提供了关于J2SE 6平台中所有的新功能。)

    这篇文章中,我们仅仅关注Java SE 6中对于Web Services规范的升级以及JAX-WS(Java API for XML Web Services)2.0规范,这些升级使得我们Web Services的创建和调用变得更加容易。使用这些新功能,我们可以仅仅使用简单的Annotations注释从一个Java类创建Web Services;随后,我们在调用这个服务的时候使用JAX-WS2.0.我们同样可以给这个服务添加一个管理者用来截取这个服务调用而且将截取的 SOAP消息传输到System.out从控制台打印出来。

    事实上,这些功能曾在JSR181(Java Specification Request 181)和JSR224 (JAX-WS)规范中已经被认可了,只是Java的正式版本拥有这些功能使它更加主流;我们同样期待Java IDE开发平台可以对这些功能进行良好的支持。

    JavaEE5规范中,允许基于标准规范让应用程序开发人员为Web Services提供服务端平台,JSR181和JSR224就是JavaEE5规范中的一部分。理论上讲,若在不改变源代码使用支持这些相同规范的应用程序服务器 ,在Java SE 6环境中大规模地发布工程这些功能将会破坏早期的Web Services应用程序,这个最初是不希望的。

我们的“野马”Web Service:服务端和客户端
在我们稳定这匹野马之前,我们先下载 这篇文中重提及的zip文件,它主要包括下边四个文件:
mustangws.zip包括了这篇文章中提及的Web Service服务端应用程序源代码,构建文件和脚本文件。
mustangwsclient.zip包括了客户端的源代码,构建文件和脚本文件。
mustangws.jar包含了已经编译过的服务端应用程序。
mustangwsclient.jar包含了已经编译过的客户端程序。

注意:你需要Java SE 6和Apache Ant这两个软件来运行这篇文章中的例子。

    在你解压之后,你将会看到两个文件架,mustangws 和mustangwsclient,这两个文件架内分别是Web Services的服务端和客户端。两个项目里面都有同样的src文件夹,里面包含了Java的源文件和Apache Ant软件需要的构建文件build.xml。这里还有附加的wsgenMustang.bat 和wsimportMustang.bat两个批处理文件,里面包含了生成Web Services存根的控制台命令,我们将在下边的章节中讲到。Apache Ant的build.xml文件对于两个应用程序而言都是放在mustangws 和mustangwsclient两个根目录中的。两个文件中有同样的ant任务init,compile,dist,clean和run。

    我们Web Services的服务端根目录里面还有一个目录wsdl,里面放了通过wsgen生成的描述Web Service的WSDL(Web Services Description Language)文件。


Web Services Metadata

    Web Services Metadata详细说明了Annotations的使用,这个在JavaSE5中就被引入了。这些对你而言可能是新概念,Annotations就是能 够被源代码使用的注释;一般使用在类定义,函数,函数参数或者函数返回值中。这些Annotations实际上就是对编译器,Java虚拟机和应用程序服务器 的 一种指示或者暗示,告诉它们应该如何处理这些有注释的代码。Annotations语句使用一般是“@”开始后面跟着Annotation文本内容。比 如,@Deprecated注释用于在使用函数的过程中将函数定义时生成的警告去掉,这一点使得Annotations变成了第一个Java类中的构造。

    当Web Services Metadata Annotations用于Java源代码中的类定义和函数定义的时,配置Web Services将会相对简单。Web Services Metadata规范将会跟着“以Java开始的”开发模式,这些意味着你首先定义一个Java类和函数,然后就对他们使用一个Web Services Metadata Annotation。对于Java开发者来说,比起通过WSDL文件创建Web Service来说通过Java类使用Annotation是创建Web Services更加自然的方法。这些Annotations注释用来暗示Web Services的运行时引擎将Java类和它里面的函数解释成为Web Service和相应的Web Service操作。将这个引擎已经绑定在Java SE 6里面了,但是因为Web Services Metadata仍然是Java EE 5里面的一部分,所以你将看到众多厂家还是提供了支持Annotation的应用程序服务器。

我们的“野马”Web服务端

    我们不再啰嗦了,让我们通过“野马”Web Service的服务端的一部分来看看Web Services Metadata在实践中的使用。浏览mustangws/src/com/techyatra/hellows目录。你将会看到下边的文件:

HelloServer 
Bootstrap 
TraceHandler 
HelloException 
Person 



    打开HelloServer文件,里面包含了Web Services Metadata Annotations定义,将一个简单的Java类用作Web Service,下边是一个实现了Service的bean文件:

package com.techyatra.hellows;

@WebService(name="HelloServer",targetNamespace=http://mustangws.techyatra.com/
, serviceName="HelloService")
@SOAPBinding(style=SOAPBinding.Style.RPC)
public class HelloServer
{   
   @WebMethod(operationName="hello", action="urn:hello")
   public@WebResult(partName="result")String ping(@WebParam(partName="person",
mode=Mode.IN,targetNamespace="http://mustangws.techyatra.com/") Person person) 
   throws HelloException
   {
        if (person == null)
        {
            System.out.println("function: hello(null)... throwing exception");
            throw new HelloException("0001", "Person is null");
        }
        else
        {
            System.out.println("function: hello(person.getTitle()
 + person.getName())");
            return "Hello. " + person.getTitle() + person.getName() +  "!";
        }
    }    
}
 在上边的代码中,@WebService注释使得类MustangServer实现了Web Service,而且@WebMethod标识类中ping方法作为WebService的操作,ping除开返回greeting的值就不会做其他的事情。

    
需要注意的是:不管你愿意不愿意,你都不能任意地将Annotation用于任何一个Java类和函数使其暴露为一个WebService和一个
WebService操作。因为Java类若要成为一个实现了WebService的bean,它需要遵循下边这些原则:
这个类必须是public类
这些类不能是final的或者abstract
这个类必须有一个公共的默认构造函数
这个类绝对不能有finalize()方法
若要成为一个实现了WebService的Bean这个Java类必须遵循这些原则:
这
个类必须是public,它的参数、返回值、和异常在每个JAX
RPC规范中都描述了Java转化成XML/WSDL映射文件的规则等等。参数和返回值可以是原始类型、数组等等;异常都可以继承Exception;请
查阅Java API去看基于XML的远程调用可以知道更多的信息。

   
现在我们已经拥有了合理规范,让我们接近代码来看看WebService
Annotation的作用。注意:这篇文章不是描述所有Annotation 和所有的WebService
Metadat规范里面的所有成员。如果要了解细节自己去查阅JSR 181规范。

    
这里有各种类型的Annotation。@WebService和@WebMethod是WSDL映射Annatotion。这些Annotation将
描述Web Service的WSDL文档元素和Java源代码联系在一起。@SOAPBinding是一个绑定的annotation用来说明网络协议
和格式。

@WebService annotation的元素name,serviceName和targetNamespace成员用来描述wsdl:portType,wsdl:service,和targetNameSpace生成WebService中的WSDL文件。

@SOAPBinding是一个用来描述SOAP格式和RPC的协议
的绑定Annotation。

@WebMethod Annotation的operationName成员描述了wsdl:operation,而且它的操作描述了WSDL文档中的SOAPAction头部。这是客户端必须要放入到SQAPHeader中的数值,SOAP 1.1中的一种约束。

@WebParam Annotation的partName成员描述了WSDL文档中的wsdl:part。

@WebResult Annotation的partName成员描述了wsdl:part用来返回WSDL文档的值。

    HelloException类是一个含有属性description和错误代码的异常类,Person是一个只有lastname和firstname 属性的简单类。这些类都没有被任何Annotation注释过。

     我们在HelloException中使用@WebFault确实很诱人,可是并非如此!Annotation是被wsgen用来生成异常bean。更加有趣的是,这个不是JSR 181规范中的一部分,但是是JAX-RPC 1.1中的一部分。

   
在我们转移到wsgen之前,一个很好的功能需要我们注意的是我们如何重载一个函数使得它成为一个WebService的操作。我们通过不同的
@WebMethod的operationName元素值来激活这个功能,这里的重载函数hello()和hello(person)有了不同的
operationName元素各自命名为hello和quickHello。

     现在为我们的服务应用程序运行Apache Ant的编译工作。利用wsgen通过这样的方式来生成build文件夹用来包含编译过的class文件和一个wsdl文件夹用来放置WSDL文档。

wsgen



   
拥有Web Service Annotation的HelloServer
类无法成功发布因为它包含了额外的没有被注释为WebService的HelloException类或者没有绑定Java
XML,如果HelloServer的函数没有抛出异常,我们就不能使用wsgen。尽管如此,在实际应用中,这种做法是不推崇的;因此使用wsgen的
目的是为了创建一个能够使用WebService的类。

   
wsgen是一个命令行功能用来生成合适的JAX-WS。它读取WebService的终端类文件,在我们的例子中就是
com.techyatra.hellows.HelloServer,同时生成所有用于WebService发布的源代码文件和经过编译过的二进制类文
件。它还随意生成WSDL和符合规范的HelloServer类WebService。wsgen从资源文件生成一个完整的操作列表是合法的。

    为了方便,这篇文章的源代码mustangws文件夹中包含了一个wsgenHello.bat批处理文件,里面包含了我们的Server项目中所需要的wsgen命令行的操作。

   
运行mustangws中的mustangws/wsgenHello.bat文件产生HelloExceptionBean源文件和编译过的class
文件放入com.techyatra.hellows.jaxb文件夹中。这些类绑定了关于Java-XML的所有操作。

    
现在我们有了发布WebService的所有需要的条件,在我们发布这个服务之前,让我们再看一看将运行过程加入到WebService的调用队列。这个
相当敏捷万一所有的通用过程都是调用这些服务所需要的过程,如此执行过程对所有的操作者来说都是合法的。操作者允许应用程序介绍自定义和核心商业需求域之
外的特殊处理。这个操作者同样会执行安全
检查,据使用统计,编码和解码数据,提供给其它模块开发者使用。

Handlers


   
从客户端(服务消费者)发送到服务端(服务提供者)的SOAP消息通过Java SE6中的JAX-WS处理管道来完成。Java SE
6提供了一个位于javax.xml.ws.handler包内的名为Handler的操作者解释器,能够插入到执行管道用来执行自定义、特殊应用程序程
序传送绑定或者没有绑定的消息。两种Handler的类型都是合法的:logical和SOAP。SOAP操作者能够操作所有完整的SOAP消息,包括
SOAP头部和SOAP的主体,而logical操作者仅仅能够操作SOAP主体有负载的部分。

    我们的服务器包括了com.techyatra.hello.TraceHandler SOAP 操作者。它解释了没有绑定和经过绑定的SOAP消息,而且将内容输出在System.out显示出来:
/*
 * TraceHandler.java
 * @author shahga
 */
package com.techyatra.hellows;
...
public class TraceHandler implements SOAPHandler<SOAPMessageContext> 
{
...    
    public boolean handleMessage(SOAPMessageContext messageContext) 
    {
        trace(messageContext);
        return true;
    }
    
    public boolean handleFault(SOAPMessageContext messageContext) 
    {
        trace(messageContext);
        return true;
    }
   ...
    
    private void trace(SOAPMessageContext messageContext) 
    {
        Boolean outMessageIndicator = (Boolean)        
        messageContext.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        
        if (outMessageIndicator.booleanValue()) 
        {
            System.out.println("\nOutbound SOAP:");
        } 
        else 
        {
            System.out.println("\nInbound SOAP:");
        }
        
        SOAPMessage message = messageContext.getMessage();
        try 
        {
            message.writeTo(System.out);
            System.out.println("");   
        } 
        catch (Exception exp) 
        {
         System.out.println("Exception in TraceHandler:trace(messageContext) : "
 + exp);
        }
    }
}


    TraceHandler实现了javax.xml.ws.handler.SOAPHandler< c
extends MessageContext>
接口。方法handleMessage()和handleFault()将未绑定和没有绑定的SOAP消息传输到System.out管道。这个功能提供
给SOAPMessageContext,这部分聚合了所有SOAP消息,它包含了各种消息属性和SOAP消息本身。
 现在我们有一个准备好了操作者的WebService,我们应该如何包装和部署这样的服务呢?这个服务打包成下边我们要讨论的一个jar文件,但是看起来更加兴奋。

Boostrap



   
在JavaSE6种,你将会看到javax.xml.ws包里面包含了终端类,这些专门用来在JVM中部署WebService。当然,听起来很正确!不
需要任何服务器。正好利用终端类创建一个服务实例,就像下边显示的Bootstrap类一样。终端是用于小的应用程序的,应用服务器消耗是不合理的,这里
是仅仅用作发布目的。

package com.techyatra.hellows;
...
public class Bootstrap 
{
    ...
    public static void main (String [] args) throws Exception
   {
        HelloServer server = new HelloServer();
           
        Endpoint endpoint = Endpoint.publish(http://localhost:9090/HelloServer,
 server);
        
        Binding binding = endpoint.getBinding();      
        List<Handler> handlerChain = new LinkedList<Handler>();
        handlerChain.add(new com.techyatra.hellows.TraceHandler());
        binding.setHandlerChain(handlerChain);
   }   
}

    这个Bootstrap类是一个便利的类用来聚合HelloServer WebService中的各个块。它发布这个服务器到一个URL地址而且将TraceHandler这个追踪操作者添加到这个WebService运行管道中。

服务应用jar文件



    运行Apache Ant中的命令创建编译过的JAR。这个dist任务创建一个MustangWS.jar文件用来标志com.techyatra.hellows.Bootstrap 类作为一个类包含了main()的入口点。

   
现在执行Apache
Ant的run命令。这个将调用Bootstrap中的main()方法用来将WebService发布到http://localhost:9090
/HelloServer。在你的浏览器里面输入http://localhost:9090/HelloServer?WSDL用来显示动态生成的
WSDL文档的内容。让我们看看WebService Metadata Annotation在WSDL中如何来提供各种服务的。

“野马”WebSerice客户端


   现在我们将HelloServer发布成为了一个WebService,让我们看看如何来调用它。当然这里有很多方法来完成这个工作,我们将使用JavaSE 6中的JAX-WS2.0中的功能来调用HelloServer这个WebService。

这
个客户端应用程序在mustangwsclient
文件夹中可以找到。除开我们熟悉的src目录和build.xml文件以外,mustangwsclient目录中还包含了
wsimportHello.bat批处理文件,用来生成JAXB的绑定存根。客户端通过存根能够很方便地调用这个服务。Java SE
6利用wsimport功能绑定生成这个存根,wsimportHello.bat使用这些功能为HelloService生成存根。

wsimport



   
wsimport能够检查WSDL文件中的特别URL是否合法然后生成所有的class用来让客户端更加清晰和容易懂得。有些人想使用SAAJ,JAXB
或者straight-up
XML用来调用WebService,可是需要小心的是,基于的wsimport的存根提供了一个方便的层次来管理WebService。它用来生成合适
的JAX-WS机制,标识这些类可以和比较规范的应用程序服务器和JVM来工作。

   
当你运行mustangwsclient文件夹中的wsimportHello.bat批处理文件,它将为Mustang
WebService客户端项目生成构建文件夹然后能够使用关键的参数运行wsimport,这些用来生成存根文件,并且将其放在他们的
com/techyatra/helloclient/jax文件夹中。若要知道细节,就查阅网上的资源。

HelloServer是一个服务端的终端接口。
HelloService是一个服务
HelloException是WSDL文档中wsdl:fault映射过来的异常类。Person类是从WSDL文件中的Person模式中生成的Java类的绑定映射文件。
这些存根将会被HelloService客户端应用程序使用,这些应用位于com.techyatra.helloclient.JAXClient 和 com.techyatra.helloclient.Client.

     客户端应用程序主要的入口点就是com.techyatra.helloclient.Client类,它接受命令行的参数,有需要的话执行参数重载和检查,创建一个Person对象,最后经过JAXClient来调用这个对象。
package com.techyatra.helloclient;
public class Client 
{
      public static void main(String[] args) throws Exception
      {                         
...
         if (args[0].equals("jax"))
         {
          com.techyatra.helloclient.jax.Person person =
       new com.techyatra.helloclient.jax.Person();
          person.setTitle(args[1]);
          person.setName(args[2]);
          JAXClient jaxClient = new JAXClient();
          jaxClient.invoke(person);
         }
      }    
}

package com.techyatra.helloclient;
import com.techyatra.helloclient.jax.*;
public class JAXClient 
{
...
    public void invoke(Person person) throws Exception
    {
        
         HelloService service = new HelloService();
         HelloServer server = (HelloServer) service.getHelloServerPort();
         String ret = server.hello(person);
         System.out.println(ret);         
    }    
}

现在,执行Ant run命令将显示下面的信息:
[java] No arguments specified. Overriding arguments to – jax Mr. Doe
[java] Alive and well. Thank you and hello  Mr. Doe!

但是这正是我们想要的!
尽管我们没有在这篇文章中讨论到,但是基于Axis客户端框架的代码和WSDL2Java脚本文件都可以下载
到,
基于Axis框架的客户段源代码在mustangwsclient/com/techyatra/hellowsclient
/AxisClient.java,而且包含了命令行参数的批处理文件wsdl2javaHello.bat,能够在mustangwsclient根目
录中找到。若要删除Axis引用,直接删除mustangwsclient/com/techyatra/helloclient/axis目录和
mustangwsclient/com/techyatra/helloclient/AxisClient.java 源文件。

让我们回顾一下我们这里的WebService进行部署:
解开源代码包。
针对我们的服务器应用程序:
执行Ant编译任务。
执行wsgenMustang.bat批处理文件生成所需要的文件。
执行Ant run任务调用来构建MustangWS.jar文件。
我们现在可以在http://localhost:9090/HelloServer中运行HelloServer服务,而且键入http://localhost:9090/HelloServer?WSDL还能够看到动态生成的WSDL文档内容。
针对客户端应用程序:
如果对Axis没有兴趣就删除mustangwsclient/com/techyatra/helloclient/axis目录和mustangwsclient/com/techyatra/helloclient/AxisClient.java 源文件。
运行wsImportHello.bat批处理文件生成客户端存根。
运行Ant的run任务就可以了。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics