文档章节

JAX-WS WS-Addressing和Stateful

xpbug
 xpbug
发布于 2014/05/06 00:54
字数 1960
阅读 830
收藏 5

回顾之前的文章,WebService SOAP消息是通过HTTP协议传输的。这个消息传给谁,以及需要执行什么action,均是由http header中的值决定:

POST /library/service HTTP 1.1/POST                    
Host: localhost:8080
SOAPAction: http://library.mycompany.com/Library/addBookRequest

这使得一个SOAP message,依赖于传输协议。如果不适用HTTP,而使用SMTP,那么需要重新设计程序对SMTP的解析。

为了杜绝消息对传输协议的依赖,W3C组织退出了WS Addressing规范。

WS Addressing规范

Web服务寻址为Web服务提供一种与传输层无关的,传送寻址信息的机制。规范主要由两部分组成:传送Web服务端点的引用的数据结构,以及一套能够在特定的消息上关联寻址信息的消息寻址属性。

其主要做法是在SOAP消息的Head中,嵌入寻址相关的属性:

  1. 目的地 -- 该消息的目的地的URI。

  2. 源端点 -- 发出该消息的服务端点(EPR - EndPoint Referrence)

  3. 应答端点 -- 应答消息接收者的端点(EPR)

  4. 故障端点 -- 故障消息接收者的端点(EPR)

  5. 动作-- 指示该消息的语义(可能有助于该消息的寻址)的URI 如http://library.mycompany.com/Library/addBookRequest

  6. 消息ID -- 唯一消息标识符UUID

  7. 关系 -- 与之前消息的关系(之前消息的UUID)

通过以上做法,可以使得SOAP消息完全自成一个自给自足小世界。脱离了任何传输协议,任何一个agent都可以根据message知道它来自哪,去向哪。这就产生了以下几种实用场景:

  • 非HTTP协议传输。如SMTP。

  • 复杂的消息交换模式(MEP)。例如,一个通过HTTP发出的消息,可以通过SMTP得到应答。再例如,场景对话。

  • 有状态的Web服务。以寻址服务作为基础,可以轻松实现有状态的Web服务。

接下来,我们以开发实例来说明寻址功能。

WS-Addressing实例

WS-Addressing的开发与WS开发没区别,可以从JAVA开始,也可以从WSDL开始。作为一个Java程序员,最简单的还是从java开始。

服务器端

package com.mycompany;

import javax.jws.WebService;
import javax.xml.ws.Action;
import javax.xml.ws.soap.Addressing;

@WebService(targetNamespace="http://mycompany.com")
@Addressing(enabled=true, required=true, responses=AddressingFeature.Responses.ALL)
public class Say {
    @Action (input="http://mycompany.com/say/helloJava/request", 
    		output="http://mycompany.com/say/helloJava/response")
    public String helloJava() {
        return "Java";
    }
    
    // 如果没有声明Action,则由程序自动产生。算法为
    // [target namespace][delimiter][port type name][delimiter][input|output name]
    public String helloPython() {
        return "Python";
    }
}

通过上面的代码,可以看到使用annotation @Addressing可以开启WS Addressing服务,使用@Action可以命名动作名称。

@Addressing有三个属性,这三个属性都是可选:

  1. enabled, 默认是true,表明开启addressing。

  2. required, 默认是false, 表明是否使用addressing功能。如果enabled和required都是true,那么SOAP header中必须出现addressing属性(MAP)。

  3. responses, 默认为ALL,如果使用非匿名则表明消息发起方不能使匿名的,必须有EPR。

部署

之前的文章已经多次写过部署步骤,往后不再重复。

生成sun-jaxws.xml,

<endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'>
    <endpoint
        name='say'
        implementation='com.mycompany.Say'
        url-pattern='/service'/>
</endpoints>

然后将代码编译打包成say.war,然后部署到tomcat。

访问 http://localhost:8080/say/service?wsdl 

查看WSDL

观察WSDL,看看开启了WS Addressing的WSDL与以往有何不同。

首先,新的WSDL比以往多了policy的声明。在Policy中,声明WS addressing不允许匿名访问。

其次,在portType中operation中的input和output,均多了wsam:action属性,这个属性会显示在SOAP header中的action中。

最后,binding中会声明port使用WS Addressing。

客户端

使用wsimport命令,生成客户端程序。之前的文章做过太多叙述,从今往后,省去wsimport相关描述。

调用客户端程序:

public class App {
    public static void main( String[] args )  {
        test2();
    }
    
    public static void test2() {
    	Say say = new SayService().getSayPort(new AddressingFeature(true,true,Responses.NON_ANONYMOUS));
    	System.out.println(say.helloJava());
    	System.out.println(say.helloPython());
    }
}

运行客户端程序。

消息

接下来,让我们查看上面的程序,客户端和服务器端交换的消息体:

1,say.helloJava()请求

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://www.w3.org/2005/08/addressing" >
  <S:Header>
    <wsa:To>http://localhost:8080/say/service</wsa:To>
    <wsa:Action S:mustUnderstand="1">http://mycompany.com/say/helloJava/request</wsa:Action>
    <wsa:ReplyTo>
      <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
    </wsa:ReplyTo>
    <wsa:MessageID>uuid:3fff81b3-2573-40b8-87f6-2b77b9e84d94</wsa:MessageID>
  </S:Header>
  <S:Body>
    <ns2:helloJava xmlns:ns2="http://mycompany.com"/>
  </S:Body>
</S:Envelope>

2,say.helloJava()响应

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://www.w3.org/2005/08/addressing" >
  <S:Header>
    <wsa:Action S:mustUnderstand="1">http://mycompany.com/say/helloJava/response</wsa:Action>
    <wsa:MessageID>uuid:ba81b661-6ca8-4d34-80f6-f260d367fc48</wsa:MessageID>
    <wsa:RelatesTo>uuid:3fff81b3-2573-40b8-87f6-2b77b9e84d94</wsa:RelatesTo>
    <wsa:To>http://www.w3.org/2005/08/addressing/anonymous</wsa:To>
  </S:Header>
  <S:Body>
    <ns2:helloJavaResponse xmlns:ns2="http://mycompany.com">
      <return>Java</return>
    </ns2:helloJavaResponse>
  </S:Body>
</S:Envelope>

3,say.helloPython()请求

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://www.w3.org/2005/08/addressing" >
  <S:Header>
    <wsa:To>http://localhost:8080/say/service</wsa:To>
    <wsa:Action S:mustUnderstand="1">http://mycompany.com/Say/helloPythonRequest</wsa:Action>
    <wsa:ReplyTo>
      <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
    </wsa:ReplyTo>
    <wsa:MessageID>uuid:48501be4-8f46-4cbc-bae0-f14c4d4f3ec0</wsa:MessageID>
  </S:Header>
  <S:Body>
    <ns2:helloPython xmlns:ns2="http://mycompany.com"/>
  </S:Body>
</S:Envelope>

4,say.helloPython()响应

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://www.w3.org/2005/08/addressing" >
  <S:Header>
    <wsa:Action S:mustUnderstand="1">http://mycompany.com/Say/helloPythonResponse</wsa:Action>
    <wsa:MessageID>uuid:5ccd13ef-db6d-4857-b847-954d4c763fa4</wsa:MessageID>
    <wsa:RelatesTo>uuid:48501be4-8f46-4cbc-bae0-f14c4d4f3ec0</wsa:RelatesTo>
    <wsa:To>http://www.w3.org/2005/08/addressing/anonymous</wsa:To>
  </S:Header>
  <S:Body>
    <ns2:helloPythonResponse xmlns:ns2="http://mycompany.com">
      <return>Python</return>
    </ns2:helloPythonResponse>
  </S:Body>
</S:Envelope>

从上面4个消息,可以看出Action, MessageID, RelatesTo, To, ReplyTo的含义。

如果需要使用NON_ANONYMOUS response的时候,我们需要使用Handler或者直接创建Dispatch的方法,来控制Header中的ReplyTo元素。wsimport所产生的portType是无法支持NON_ANONYMOUS的。

Stateful

WebService与Servlet一样,是无状态的。每个SEI只会有一个实例,不同的request会启动不同的线程,但调用的是同一个SEI实例。然而,JAXWS RI的vendor提供了有状态的机制,使得WebService也可以有状态。这样每个client可以维持一个针对自己的SEI实例。

WebService stateful是依靠WS addressing而建。若想实现Stateful,需要做到以下3点:

  1. 使用annotation @Stateful @WebService @Addressing

  2. 声明public static StatefulWebServiceManager<T> manager。

  3. 使用另一个SEI,初始化Stateful的EndPoint,然后将EPR以W3CEndpointReference的类型暴露出去。

实例 - 服务器端

银行账户

package com.mycompany;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.ws.soap.Addressing;
import com.sun.xml.ws.developer.Stateful;
import com.sun.xml.ws.developer.StatefulWebServiceManager;

@Stateful
@WebService
@Addressing
public class Account {
	public static StatefulWebServiceManager<Account> manager;
    protected final int id;
    private int balance;

    public Account(int id) {
        this.id = id;
    }

    @WebMethod
    public synchronized void deposit(int amount) {
        balance += amount;
    }
    
    @WebMethod
    public synchronized int get() {
        return balance;
    }
    
    @WebMethod
    public void logout() {
    	manager.unexport(this);
    }
}

银行

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.ws.wsaddressing.W3CEndpointReference;

@WebService
public class Bank { 

    @WebMethod
    public synchronized W3CEndpointReference login(int accountId) {
        Account acc = new Account(accountId);
        return Account.manager.export(acc);
    }    
}

打包部署

sun-jaxws.xml

<endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'>
    <endpoint
        name='account'
        implementation='com.mycompany.Account'
        url-pattern='/account'/>
    <endpoint
        name='bank'
        implementation='com.mycompany.Bank'
        url-pattern='/service'/>
</endpoints>

在此例中,无法使用wsgen,因为WebService默认必须存在默认构造器,而Account使用了带参数的构造器。没关系,那我们就不使用wsgen了,直接将上面两个java编译好的class和sun-jaxws.xml一起打包成bank.war,然后部署就好了。

部署完成,请访问http://localhost:8080/bank/service 

实例 - 客户端

使用wsimport,对两个WSDL进行转换。

然后在App.java中调用所产生的类。

public class App {
    public static void main( String[] args )  {
        Bank bank = new BankService().getBankPort();
        Account account1 = new AccountService().getPort(bank.login(1), Account.class);
        Account account2 = new AccountService().getPort(bank.login(1), Account.class);
        account1.deposit(100);
        account1.deposit(300);
        account2.deposit(200);
        account2.deposit(100);
        
        System.out.println("Account 1 has "+account1.get());
        System.out.println("Account 2 has "+account2.get());
        account1.logout();
        account2.logout();
    }
}

保持状态的原理很简单,stateful的EndPoint会为每个新的request创建新实例,SOAP Header中除了保留了Addressing的信息,还多了object的信息。这样,object可以在服务器端被找回。

<jaxws:objectId xmlns:jaxws="http://jax-ws.dev.java.net/xml/ns/" xmlns:ns2="http://mycompany.com/" xmlns:ns3="http://www.w3.org/2005/08/addressing" xmlns:wsa="http://www.w3.org/2005/08/addressing" wsa:IsReferenceParameter="1">5198581b-bbc9-4b30-be54-6d4d969ea784</jaxws:objectId>


© 著作权归作者所有

上一篇: JAX-WS 文件传递
下一篇: JAX-WS 异步
xpbug
粉丝 304
博文 101
码字总数 124566
作品 0
浦东
程序员
私信 提问
Web服务框架--JBossWS

JBoss WS (JBoss Web Services)实现了一个新的符合JAX-RPC1.1标准的SOAP stack,可用于替代先前的WS4EE和JBoss.NET。它能够更好的与整个JBoss架构体系相集成。 支持: JAX-RPC and JAX-WS (...

匿名
2008/09/13
2.4K
0
JBossWS 5.0.0.Final 发布,JBoss Web 服务堆栈

JBossWS 5.0.0.Final 发布了,该版本包含众多新特性和 bug 修复。值得关注的包括:升级到 Apache CXF 3,带来很多 Apache CXF 3 的新特性和改进;简化了配置等等。更详细介绍请看之前 Beta1 ...

红薯
2015/04/28
629
0
Java的Web服务规范API--JAX-WS

JAX-WS (JavaTM API for XML-Based Web Services)规范是一组XML web services的JAVA API。JAX-WS允许开发者可以选择RPC-oriented或者message-oriented 来实现自己的web services。 在 JAX-WS......

匿名
2010/08/27
5.6K
1
与Java Web Service相关的若干概念(JAX-WS,JAX-RS)

l JWS: 是指与webservice相关的J2EE(其实现在应该叫做Java EE吧)技术叫做 JWS(全称就是 java webservice)。没错,jws指的就是 javawebservice l Jws含有的技术 1. JAX-WS 2. JAX-RS 3. JAX...

豆豆4997
2014/04/09
97
0
Apache CXF 和 Spring 开发 Web Service 1

正文 为什么使用CXF 本文段摘录自 http://www.ibm.com/developerworks/cn/webservices/ws-pojo-springcxf/index.html Apache CXF 是一个开放源代码框架,提供了用于方便地构建和开发 Web 服务...

洋葱94
2015/12/04
268
0

没有更多内容

加载失败,请刷新页面

加载更多

vue vue-router beforeRouteEnter

本文转载于:专业的前端网站➬vue vue-router beforeRouteEnter beforeRouteEnter (to, from, next) { // 在渲染该组件的对应路由被 confirm 前调用 // 不!能!获取组件实例 `thi...

前端老手
51分钟前
4
0
Spring Boot 2 实战:结合 Docker 容器化

1. 前言 Docker 是啥?好处是啥? 什么现在如果作为一名开发你还不知道甚至没有用过我都感觉你落伍了。Docker 是一种虚拟化的容器技术,目的为了打造持续集成、版本控制、可移植性、隔离性和...

码农小胖哥
55分钟前
4
0
Linux 常用查询句柄的命令(Too many open files)

摘要 查询命令 进程占用的句柄总数 lsof -n | wc -l 查看哪一个进程占用的句柄最多 lsof -n|awk '{print $2}'|sort|uniq -c|sort -nr|more 第一列 是 占用句柄数; 第二列 是 进程号 PID;...

liangxiao
55分钟前
5
0
JavaScript权威指南笔记5

第五章、语句 0、概述 JS程序是一系列可执行语句的集合。 通过控制语句来改变语句的默认执行顺序。 控制语句:条件、循环和跳转(如return、break和throw) 2、复合语句 当多条语句被当做一条语...

_Somuns
今天
5
0
vmware安装ubuntu18.04总是 panic -not syncing:corrupted stack end detected inside schedule

Vmware 安装ubuntu 总是卡着不动, 提示panic -not synciong 网上提示很多办法,都试了效果不佳, 找到了完美解决办法 你的兼容模式重新选一下为6.0,兼容性对硬件有要求的 即可实现 成功安装...

dragon_tech
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部