duduwolf / 编程 / 使用 xfire 快速发布 WebService

分享

   

使用 xfire 快速发布 WebService

2006-10-11  duduwolf
使用 xfire 快速发布 WebService

在经历过早年 Delphi, C++, Java 等不成熟环境中开发 WebService 的折磨之后,接触 ASP.NET 的最大感触,就是 WebService 的开发门槛被大大降低了。无需对 SOAP 或基本架构有任何了解,仅需简单的定义几个 Attribute 即可,所有的 dirty work 都由 ASP.NET 在后台自动完成。例如:
以下内容为程序代码:

[ WebService(Description="Common Server Variables",Namespace="http://flier.yeah.net/"[img]/images/wink.gif[/img]]
public class ServerVariables: WebService {


  [ WebMethod(Description="Obtains the Server Computer Name",EnableSession=false)]
  public string GetMachineName() {
     return Server.MachineName;
  }   
}

而这一巨大的生产力进步,现在开始在 Java 领域也可直接享用。例如:
以下内容为程序代码:

/**
 * Common Server Variables
 * 
 * @@WebService(name = "ServerVariables", serviceName = "ServerVariables", targetNamespace = "http://flier.yeah.net/"[img]/images/wink.gif[/img]
 * @@SOAPBinding(style = SOAPBindingAnnotation.STYLE_RPC)
 */
public interface ServerVariables
{
  /**
   * @@WebMethod(operationName = "GetMachineName", action="urn:GetMachineName"[img]/images/wink.gif[/img]
   * @@.return WebResult("machineName"[img]/images/wink.gif[/img]
   */
  string GetMachineName();
}

在项目搭建时完成一次性的配置之后,我们绝大多数的工作就是定义 WebService 接口,设定 WebService 的发布方式。然后就是实现此接口并放入 spring 中进行管理,例如:
以下内容为程序代码:

<bean id="serverVariables" class="net.yeah.flier.ws.impl.ServerVariablesImpl" singleton="true">

这一神奇效果的幕后英雄就是 codehaus 最新发布的 xfire 1.0 SOAP 框架。
与 Axis 等现有实现相比,xfire 的最大优点就是改用 StAX 基于流的 XML 分析引擎,加上其 SOAP 协议栈实现上的精巧设计,能够带来比 Axis 快 2-5 倍的性能提升。具体的性能评测数据,可以参考  一些第三方评测结果 
而在功能上 xfire 也毫不逊色,其 Features & Goals 里面充满了各种 big words。
以下为引用:

* Support for important Web Service standards - SOAP, WSDL, WS-I Basic Profile, WS-Addressing, WS-Security, etc.
* High performance SOAP Stack
* Pluggable bindings POJOs, XMLBeans, JAXB 1.1, JAXB 2.0, and Castor support
* JSR 181 API to configure services via Java 5 and 1.4 (Commons attributes JSR 181 syntax)
* Support for many different transports - HTTP, JMS, XMPP, In-JVM, etc.
* Embeddable and Intuitive API
* Spring, Pico, Plexus, and Loom support.
* JBI Support
* Client and server stub generation
* JAX-WS early access support


与 Axis, Glue 以及 ActiveSOAP  的详细功能对比,可以参考 Stack Comparison 文档。

下面先就 xfire 基础架构以及与 spring 集成的基本情况大概做一个简单介绍,回头有时间再整一个 xfire 与 axis 实现架构的横向对比。
与 axis 很相似 xfire 在架构上也可以大概分为 Service, Transport 和 Invoker 三个层面。
Service 层是 xfire 架构的静态基础,负责完成对服务的注册及其管理。核心的 ServiceRegistry 接口完成对服务自身的生命期管理,如注册/注销/获取等等;而 ServiceFactory 接口则负责从具体的 POJO 类型,生成实现  Service 接口的可被管理的服务代理。
Transport 层则是 xfire 的外部 IO 处理系统。由 TransportManager 接口对具体的 Transport 接口实现进行管理,默认提供了基于 pipe 的 LocalTransport 和基于 Http 协议的 SoapHttpTransport。理论上可以任意进行扩展,例如 xfire 发布包中还提供了基于 JMS 和 XMPP 的实现。
Invoker 则是 xfire 的动态调用层,负责在 Transport 层接受到请求后,解析内容、调用合适服务并最终返回 SOAP 封包给调用者。运行时 Invoker 负责维系 Service 和 Transport 之间的联系。
因此一个服务的生成和注册往往类似如下代码:
以下内容为程序代码:

Service endpoint = serviceFactory.create(clazz); // 根据具体类型创建服务
xFire.getServiceRegistry().register(endpoint);  // 向服务管理注册服务
endpoint.setInvoker(new BeanInvoker(bean));  // 设定服务调用模式

最基本的 XFire 接口,实际上就是 getServiceRegistry() 和 getTransportManager() 的封装。

xfire 中另一块核心的思想,就是其灵活而强大的 binding 机制,负责完成 Java 类型与 SOAP 消息的双向转换。xfire 仅仅内建就支持 POJO(Aegis), Castor, JAXB 1.1, JAXB 2.0 和 XMLBeans 多种模式,你可以根据需求选择从全自动 POJO 生成,到全手工 xsd 文件定义的不同方式。

而对使用 spring 环境的开发者来说,要提供上述这一系列支持,只需要直接将 xfire-spring 工程中的 fire.xml 文件包括到 applicationContext.xml 中,即可直接获得完整的 xfire 架构。
以下内容为程序代码:

<import resource="classpath:/org/codehaus/xfire/spring/xfire.xml"/>


不过相比于通过 xml 文件定义服务来说,我更倾向于直接用 Annotation 方式在代码级完成定义。xfire-annotation 提供了对 Java 5 Annotation 和 apache common-attribute 的完整支持。例如上述例子中在 javadoc 里定义的 @@ 标签,就是 用于使用 xdoclet 生成 common-attribute 支持的。
因为 common-attribute 并非编译器一级提供支持,因此要在编译之前增加一道处理过程。个人推荐直接使用 maven 进行管理,当然也可以在 ant 中直接配置 task。maven 1.x 的具体配置方式如下:
以下内容为程序代码:


# project.properties for maven 1.x
#
org.apache.commons.attributes.enable=true
org.apache.commons.attributes.index.enable=true
org.apache.commons.attributes.attributepackages=org.codehaus.xfire.annotations.commons;org.codehaus.xfire.annotations.commons.soap;org.codehaus.xfire.annotations.soap

其中 org.apache.commons.attributes.enable 负责启用 maven 的 common-attribute 支持;org.apache.commons.attributes.index.enable 启用索引模式,生成时会自动产生一个索引文件 META-INF/attrs.index;org.apache.commons.attributes.attributepackages 则是预定义可能用到的 annotations 的包名称,以便直接用 WebService 这样的简短名字。
增加了以上配置后,在 maven 编译时,会自动增加一个 common-attribute 支持文件的编译过程,并在 target/commons-attributes 下生成类似 ServerVariables$__attributeRepository.java 的文件,以存储 @@ 标签中指定的 Metadata。在 eclipse 等 IDE 中,我们可以直接把这些目录添加为源码目录。

而在为了使用 common-attribute 定义的元信息,xfire 提供了 Jsr181HandlerMapping 来自动完成服务映射支持。
以下内容为程序代码:

<bean id="webAnnotations" class="org.codehaus.xfire.annotations.commons.CommonsWebAttributes"/>
<bean id="handlerMapping" class="org.codehaus.xfire.spring.remoting.Jsr181HandlerMapping">
    <property name="typeMappingRegistry">
        <ref bean="xfire.typeMappingRegistry"/>
    </property>
    <property name="xfire">
        <ref bean="xfire"/>
    </property>
    <property name="webAnnotations">
        <ref bean="webAnnotations"/>
    </property>
</bean>

因为 xfire 同时需要支持 Java 5 模式和 common-attribute 模式的元信息,所以对元信息的获取被封装到 annotations.commons.CommonsWebAttributes 和 annotations.jsr181.Jsr181WebAnnotations 中,以便根据实际使用进行配置。而 xfire 和 xfire.typeMappingRegistry 则是引用前面提到的 xfire.xml 中定义的基础接口和类型映射支持。

Jsr181HandlerMapping 是从 spring 提供的 ApplicationObjectSupport 上基础出来的,它实现了 ApplicationContextAware 接口,因此会在 bean 定义被加载完成后,调用其 setApplicationContext 方法,并进而调用其 initApplicationContext 方法完成初始化。
Jsr181HandlerMapping.initApplicationContext 方法中,会遍历当前 ApplicationContext 的所有父容器,调用 processBeans 方法处理器其 bean 定义。而在 processBeans 中针对每个 bean,检查其是否为 singleton 的非抽象对象,且定义了 WebService Annotation;如果是则构造 bean 并以前面提到的方式进行注册。
值得注意的是,xfire 1.0 乃至最新 CVS 版本,在这块的实现都有一个严重 bug,没有对 abstract bean 进行处理。其代码中直接用 beanFactory.getBean(beanNames[i]) 试图获取实例,如果 bean 是 singleton 的 abstract 模式,则会导致 spring 抛出异常。可以将其检测代码改成如下方式:
以下内容为程序代码:

public class Jsr181HandlerMapping extends AbstractUrlHandlerMapping
{
    private void processBeans(ApplicationContext beanFactory, AnnotationServiceFactory serviceFactory)
    {
        String[] beanNames = beanFactory.getBeanDefinitionNames();
        
        ConfigurableApplicationContext ctxt = (ConfigurableApplicationContext) beanFactory;

        // Take any bean name or alias that has a web service annotation
        for (int i = 0; i < beanNames.length; i++)
        {
            BeanDefinition def = ctxt.getBeanFactory().getBeanDefinition(beanNames[i]);
            
            if (!def.isSingleton() || def.isAbstract()) continue;
            
            //...
        }
    }    
}    

此 bug 及修复代码已提交到 xfire 的 JIRA 上,待进一步确认。

而在具体对 bean 是否定义服务的判断上,xfire 1.0 中的支持实际上也是很不完整的。象最上面例子中,由 ServerVariables 定义服务接口,ServerVariablesImpl 中实现服务的方式,直接在 xfire 下是无法使用的。关键原因在于 xfire-annotations 工程在定义 WebService 等 annotation 时,没有指定其可继承性。
改进方法也很简单,在 org.codehaus.xfire.annotations.commons 包的 WebService, WebMethod, WebResult, WebParam 等 annotation 定义中,类一级增加一个 @@org.apache.commons.attributes.Inheritable() 标签即可。common-attribute 在判断是否存在某个 annotation 时,会自动将具有 Inheritable 属性的自定义属性,继承到其所有子类。这样一来就可以很完美的解决通过接口定义服务的问题。此改进也已提交到 xfire 的 JIRA 上,待进一步确认。

完成了这些基础性工作后,剩下的任务就非常简单了。根据 xfire 的 Servlet Setup 文档,在 web.xml 中增加相关 servlet 的定义,接管 /service/** 或其它 URL。
以下内容为程序代码:

  <servlet>
    <servlet-name>XFire</servlet-name>
    <display-name>XFire Servlet</display-name>
    <servlet-class>
        org.codehaus.xfire.transport.http.XFireConfigurableServlet
    </servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>services.xml</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>XFire</servlet-name>
    <url-pattern>/servlet/XFireServlet/*</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>XFire</servlet-name>
    <url-pattern>/services/*</url-pattern>
  </servlet-mapping>
  

然后就可以通过 ::URL::http://localhost:8080/xfire/services/WeatherService?wsdl  类似的方式访问 wsdl 或调用 WebService 了。详细的配置请参考 xfire 相关文档。
完整的 xfire 支持 bean 配置大致如下:
以下内容为程序代码:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!-- applicationContext-xfire.xml -->
<beans>
    <import resource="classpath:/org/codehaus/xfire/spring/xfire.xml"/>
    
    <bean id="webAnnotations" class="org.codehaus.xfire.annotations.commons.CommonsWebAttributes"/>
    <bean id="handlerMapping" class="org.codehaus.xfire.spring.remoting.Jsr181HandlerMapping">
        <property name="typeMappingRegistry">
            <ref bean="xfire.typeMappingRegistry"/>
        </property>
        <property name="xfire">
            <ref bean="xfire"/>
        </property>
        <property name="webAnnotations">
            <ref bean="webAnnotations"/>
        </property>
    </bean>
    
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="urlMap">
            <map>
                <entry key="/">
                    <ref bean="handlerMapping"/>
                </entry>
            </map>
        </property>
    </bean>
</beans>

如果需要使用 abstract bean 的话,可以按前面所说修改 Jsr181HandlerMapping,并重新编译 xfire-spring 工程;如果需要提供接口定义服务的支持,可以按前面所说修改 WebService 等,并重现编译 xfire-annotation 工程。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。如发现有害或侵权内容,请点击这里 或 拨打24小时举报电话:4000070609 与我们联系。

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多
    喜欢该文的人也喜欢 更多

    ×
    ×

    ¥.00

    微信或支付宝扫码支付:

    开通即同意《个图VIP服务协议》

    全部>>