分享

Web 服务编程技巧与窍门: 在 JAX-RPC 应用程序中构建有状态会话

 hrkflying 2008-11-22

2004 年 9 月 01 日

学习如何利用 servlet 端点模型来扩展无状态 JAX-RPC Web 服务,并使用 HTTP 会话来构建有状态 Web 服务应用程序。我们用一个简单的购物车 Web 服务范例来加以说明。

Web 服务开发组对于 Web 服务本身是否为无状态存在着争论。一些开发人员提出了类似于 Web 服务资源框架(WS-Resource Framework,请参阅 参考资料)那样的规范来定义有状态 Web 服务,而当前广泛使用的 JAX-RPC 规范主要处理无状态服务。 无状态的意思是,对 Web 服务的调用与之前的互操作没有相关性,并且不为以后的互操作而保存当前活动的信息。

您可以通过一些实际情况来了解无状态的含义,比如像 JavaBean 和无状态会话企业 JavaBean 组件之类的 JAX-RPC 服务端点组件并不保持状态信息。然而,您仍然可以利用基础传输协议固有的会话支持,并创建有状态 Web 服务,这取决于服务端点部署于何处。本文说明了当服务端点部署到 servlet 容器时,如何在 JAX-RPC 应用程序中使用 HTTP 的会话功能。我们将在一个简单的购物车 Web 服务中对此进行说明。

范例:购物车服务

清单 1定义了一个简单的购物车 Web 服务,它允许顾客在购物车中添加、删除和查询商品条目。一个需求是该购物车需要在整个购物期间保持已选择的商品,直到顾客付款后离开。本文的其余部分说明为什么这个简单的有状态服务可以使用多种 JAX-RPC 技术来实现。


清单 1. 购物车接口
package shopping;
                        public interface Cart extends java.rmi.Remote {
                        public void addItem(String item) throws java.rmi.RemoteException;
                        public void removeItem(String item) throws java.rmi.RemoteException;
                        public int getItemNumber() throws java.rmi.RemoteException;
                        public void checkout() throws java.rmi.RemoteException, shopping.NoUserSessionFault;
                        }
                        





回页首


服务的实现

该服务通过两个步骤实现:

  1. 实现 ServiceLifecycle 接口。
  2. 处理会话数据。

实现 ServiceLifecyle 接口

JAX-RPC 定义了一个 javax.xml.rpc.server.ServiceLifecyle 接口。如果服务实现类实现这个接口,就需要 JAX-RPC 运行时系统来管理相应的服务端点实例的生存周期。该 ServiceLifecyle 接口有两个方法: initdestroyinit 方法使用泛型类型 java.lang.Object 的“context”参数,但如果服务端点部署到基于 servlet-容器的 JAX-RPC 运行时系统,那么任何适应的 JAX-RPC 运行时系统都需要传递一个 javax.xml.rpc.server.ServletEndpointContext 实例。

在我们的范例中,服务实现类 CartSoapBindingImpl (参见 清单 2)实现了这样一个 ServiceLifecyle 接口。在实现实例进行了实例化之后,JAX-RPC 运行时调用它的 init 方法将 context 参数传给类型 javax.xml.rpc.server.ServletEndpointContext 。这是可以做到的,因为购物车服务是基于 servlet 的。然后 context 对象保存为私有字段,这样后来的调用可以使用它来获取 HttpSession 。当清除时,一旦调用 destroy 方法,context 对象就被设置为 null。


清单 2. 实现 ServiceLifecycle 接口
public class CartSoapBindingImpl implements shopping.Cart, ServiceLifecycle {
                        private ServletEndpointContext jaxrpcContext;
                        public void init(Object context) throws ServiceException {
                        jaxrpcContext = (ServletEndpointContext) context;
                        }
                        public void destroy() {
                        jaxrpcContext = null;
                        }
                        ...
                        }
                        

处理会话数据

javax.xml.rpc.server.ServletEndpointContext 接口包含几个方法。其中与本文主题有关的一个方法是 getHttpSession() ,它返回一个 javax.servlet.http.HttpSession 实例。对于每个购物车服务的方法,该方法首先针对以前缓存的 ServletEndpointContext 调用 getHttpSession 方法,以获得 HttpSession 对象。一旦 HttpSession 可用,服务实现就能够从 HttpSession 对象中检索用户会话数据,正如常规 servlet 一样(参见 清单 3)。请注意, 清单 3 中定义的 SessionData 类是用于保存各种购物车信息的用户自定义数据结构(点击本文顶部或底部的 code 图标)。


清单 3. 从 ServletEndpointContext 中检索 HTTPSession
public class CartSoapBindingImpl implements shopping.Cart, ServiceLifecycle {
                        private ServletEndpointContext jaxrpcContext;
                        ...
                        public void addItem(java.lang.String item)
                        throws java.rmi.RemoteException {
                        getSessionData().addItem(item);
                        }
                        // SessionData is a utility class to store all the shopping cart related information
                        private SessionData getSessionData() {
                        SessionData sd = (SessionData)
                        jaxrpcContext.getHttpSession().getAttribute(SessionData.SESSION_KEY);
                        if(sd == null) {
                        sd = new SessionData();
                        jaxrpcContext.getHttpSession().setAttribute(SessionData.SESSION_KEY, sd);
                        }
                        return sd;
                        }
                        ...
                        }
                        





回页首


客户端的启用

与自动处理会话的典型 Web 浏览器不同的是,JAX-RPC 客户端并不缺省的使用目标服务端点参与会话。因为这一点,参与会话的客户端必须明确的将一个专门的存根属性( javax.xml.rpc.Stub.SESSION_MAINTAIN_PROPERTY 或者 " javax.xml.rpc.session.maintain ")设置为 true,这样 JAX-RPC 客户端运行时将维护调用客户端的会话(参见 清单 4)。否则,服务端点获取的 HttpSession 对象将为空,或者各个服务调用的 HttpSession 很可能不一致。

请注意 JAX-RPC 客户端的定义是相对于服务的,这些服务与客户端进行通信,并且不同服务的存根被认为是不同的客户端。同样,即使存根可能与运行在相同后端服务器的服务进行对话,典型的 JAX-RPC 运行时也分别为每个单独的存根维护会话数据。因此,如果您编写应用程序来调用两个不同的服务,就不应该期望能够跨这两个服务调用来对会话进行维护。


清单 4. 要求 JAX-RPC 运行时维护会话
	javax.xml.rpc.Stub jaxrpcStub = (javax.xml.rpc.Stub) getVendorSpecificStub();
                        jaxrpcStub._setProperty(javax.xml.rpc.Stub.SESSION_MAINTAIN_PROPERTY, Boolean.TRUE);





回页首


结束语

即使基于 JAX-RPC 的 Web 服务本身是无状态的,您仍然可以使用 servlet 容器提供的会话支持并采用特定的服务端点模型(比如基于 servlet 的端点)来构建有状态 Web 服务应用程序。同时,客户端也需要切换一个特定的标志来通知 JAX-RPC 运行时系统对客户端会话进行维护。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多