运用Spring DM和CXF在TOMCAT上发布WebService

应用Spring DM和CXF在TOMCAT上发布WebService

1       目标

项目应用Spring DM实现模块化开发。系统和其他进程之间的控制信息希望能通过Webservice实现。要求如下:

Ø  动态发布

有多个Bundle需要发布WebService,所以希望创建一个Webservice Util(下面简称ws-util)的Bundle并提供易用的接口发布为OSGI的服务,其它Bundle可以通过调用该服务即可实现Webservice的发布。

Ø  依赖最小化

需要发布的Bundle依赖ws-util,但不需要依赖CXF,如果把CXF替换为其他的Webservice引擎,不会引起其他Bundle的变更。

Ø  编码方便

尽可能减少对业务代码的侵入。

2       思路

1,  如何实现动态发布?

需要发布的Bundle依赖ws-util,调用接口即可完成发布,所以不能在ws-util中静态配置webservice

解决的办法是利用jax-ws 中定义EndPoint接口,对该接口包装成一个类并发布为OSGI Service,其他bundle调用该service完成webservice的发布。

2,  ws-util的上下文环境是什么样的?

Project中存在WEB-INF目录,并且存在web.xml描述文件,osgitomcat bundle启动时会自动监视该目录并且加载该文件,并把ws-util作为war包部署到web容器中。

OSGI CORE启动时会监视METAINF.MF中的MANIFEST.MF文件,并初始化OSGI上下文,发布OSGI Service。所以ws-util处于双重的容器管理之下,一重是OSGI容器管理,一重是WEB容器管理。

Tomcat bundle也是由OSGI CORE启动,所以ws-utilOSGI上下文初始化要早于Web上下文的初始化,如果用META-INF/spring下的spring配置方式发布EndPoint包装接口,此时的CXF Bus还没有开始初始化,EndPoint找不到web serverprovider,发布就不会成功。

3,  如何把tomcatcxf整合起来?

       1)注册Activator, activator初始化方法中获取osgi上下文。

    2web上下文加载CxfNoSpringServlet时,初始化cxf bus后,获取缓存中的osgi上下文,并且把EndPoint包装类注册为osgi 服务。

 

3       环境配置

JDK1.6

Eclipse3.5.2

Spring3.0.6

CXF2.3.1

3.1   OSGI PlatForm的依赖Bundle

Spring 相关Bundle

org.springframework.beans-3.0.6.RELEASE.jar

org.springframework.context.support-3.0.6.RELEASE.jar

 org.springframework.web-3.0.6.RELEASE.jar

 org.springframework.context-3.0.6.RELEASE.jar

 org.springframework.asm-3.0.6.RELEASE.jar

 org.springframework.expression-3.0.6.RELEASE.jar

 org.springframework.web.servlet-3.0.6.RELEASE.jar

 org.springframework.core-3.0.6.RELEASE.jar

 org.springframework.aspects-3.0.6.RELEASE.jar

 org.springframework.aop-3.0.6.RELEASE.jar

 org.springframework.oxm-3.0.6.RELEASE.jar

Spring DM 相关Bundle

org.eclipse.osgi.services_3.2.0.v20090520-1800.jar

 spring-osgi-core-2.0.0.M1.jar

 spring-osgi-extender-2.0.0.M1.jar

 spring-osgi-annotation-2.0.0.M1.jar

 spring-osgi-web-extender-2.0.0.M1.jar

 spring-osgi-io-2.0.0.M1.jar

 spring-osgi-web-2.0.0.M1.jar

XML 相关Bundle

com.springsource.org.apache.xml.serializer-2.7.1.jar

 com.springsource.javax.xml.ws-2.1.1.jar

 com.springsource.javax.xml.stream-1.0.1.jar

 com.springsource.javax.xml.bind-2.2.0.jar

 com.springsource.org.apache.xmlcommons-1.3.4.jar

 com.springsource.javax.xml.soap-1.3.0.jar

 com.springsource.org.apache.xml.resolver-1.2.0.jar

 com.springsource.org.dom4j-1.6.1.jar

 com.springsource.org.apache.xerces-2.9.1.jar

其他依赖包

com.springsource.net.sf.cglib-2.2.0.jar

 com.springsource.javax.activation-1.1.1.jar

 javax.el-2.2.0.v201105051105.jar

 com.springsource.org.apache.commons.el-1.0.0.jar

 com.springsource.org.aopalliance-1.0.0.jar

com.springsource.org.apache.commons.logging-1.1.1.jar

Tomcat启动包

catalina.start.osgi-1.0.0.jar

 catalina.osgi-5.5.23-SNAPSHOT.jar

      

所有这些Bundle可以在http://ebr.springsource.com/repository/app/ 网站下载。

4       创建Webservice Bundle

4.1   创建Plug-in project

4.2   Project 目录结构

 

目录说明如下:

 

SRC

插件代码

Lib

CXF 依赖包

META-INF

OSGI工程的配置项

WEB-INF

当前项目要发布到Web容器中,OSGI根据该目录的Web.xml确定部署方式

4.3   CXF依赖包

Webservice lib下面的jar都是来自于cxf,但是我们用不了所有的jar包,需要根据我们的需要剔除一部分。

我们需要把webservice发布到Tomcat中,并且以jax-ws的方式,可以从cxflib中剔除jettyrestful实现相关的jar包。

另外我们的OSGI platform中已经有web相关的bundle,所以servlet相关的jar也要剔除。

剩下的jar列表如下:

lib/cxf-2.3.1.jar,

 lib/cxf-wstx-msv-validation-2.3.1.jar,

 lib/cxf-xjc-boolean-2.3.0.jar,

 lib/cxf-xjc-bug671-2.3.0.jar,

 lib/cxf-xjc-dv-2.3.0.jar,

 lib/cxf-xjc-ts-2.3.0.jar,

 lib/msv-20050913.jar,

 lib/neethi-2.0.4.jar,

 lib/oro-2.0.8.jar,

 lib/stax2-api-3.0.2.jar,

 lib/velocity-1.6.4.jar,

 lib/woodstox-core-asl-4.0.8.jar,

 lib/wsdl4j-1.6.2.jar,

 lib/xmlbeans-2.4.0.jar,

 lib/XmlSchema-1.4.7.jar,

 lib/xmlsec-1.4.4.jar

4.4   MANIFEST.MF添加需要importpackage

javax.servlet,

 javax.servlet.http;version="2.5.0",

 javax.xml.ws,

 org.osgi.framework,

 org.osgi.util.tracker,

 org.springframework.beans;version="3.0.6.RELEASE",

 org.springframework.beans.factory;version="3.0.6.RELEASE",

 org.springframework.beans.factory.config;version="3.0.6.RELEASE",

 org.springframework.beans.factory.support;version="3.0.6.RELEASE",

 org.springframework.beans.factory.wiring;version="3.0.6.RELEASE",

 org.springframework.beans.factory.xml;version="3.0.6.RELEASE",

 org.springframework.context;version="3.0.6.RELEASE",

 org.springframework.context.event;version="3.0.6.RELEASE",

 org.springframework.context.support;version="3.0.6.RELEASE",

 org.springframework.core;version="3.0.6.RELEASE",

 org.springframework.core.io;version="3.0.6.RELEASE",

 org.springframework.core.io.support;version="3.0.6.RELEASE",

 org.springframework.osgi.context;version="2.0.0.M1",

 org.springframework.osgi.web.context.support;version="2.0.0.M1",

 org.springframework.web.context;version="3.0.6.RELEASE"

 

导入结束后,会在projectplug-in Dependencies下出现下列bundle

 

4.5   JAVA 源码说明

4.5.1   类结构

说明:

WSRegister

EndPoint包装类接口

WSRegisterImpl

包装接口实现类

CXFWrapperServlet

CXFNoSpringServlet

WsActivator

OSGI启动类

ContextStore

OSGI 上下文缓存

 

4.5.2   WSRegister

package com.boco.gutil.webservice.exports;

 

public interface WSRegister {

    public void regiser(Object impl,String address);

}

 

4.5.3   WSRegisterImpl

public class WSRegisterImpl implements WSRegister {

    private static final Logger logger = Logger.getLogger(WSRegisterImpl.class

           .getName());

 

    public void regiser(final Object impl, final String address) {

       ClassLoader clsCl = this.getClass().getClassLoader();

       Thread.currentThread().setContextClassLoader(clsCl);

      

       logger.info(" Regiser Service @" + address);

      

       Endpoint.publish(address, impl);

    }

}

4.5.4   ContextStore

public class ContextStore {

    private static BundleContext context = null;

 

    public static BundleContext getContext() {

       return context;

    }

 

    public static void setContext(BundleContext context) {

       ContextStore.context = context;

    }

}

 

4.5.5   WsActivator

public class WsActivator implements BundleActivator {

   

    public void start(BundleContext context) throws Exception {

       ContextStore.setContext(context);

      

    }

 

   

    public void stop(BundleContext bc) throws Exception {

       ContextStore.setContext(null);

    }

 

}

4.5.6   CXFWrapperServlet

public class CXFWrapperServlet extends CXFNonSpringServlet {

    private static final long serialVersionUID = -6994879522066465447L;

    private static final Logger logger = Logger.getLogger(CXFWrapperServlet.class

           .getName());

   

    public void loadBus(ServletConfig servletConfig) throws ServletException {

        super.loadBus(servletConfig);       

       

        Bus bus = this.getBus();

       

        BusFactory.setDefaultBus(bus);

       

        BundleContext context = ContextStore.getContext();

       

        Dictionary<String, String> props = new  Hashtable<String, String>();

        props.put("context-class-loader", "service-provider");

       

        context.registerService(WSRegister.class.getName(), new WSRegisterImpl(), props);

        logger.info("Start Deploy com.boco.gutil.webservice");

    }

}

4.5.7   Web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee

    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"

    version="2.4">

    <context-param>

       <param-name>contextConfigLocation</param-name>

       <param-value>

           /WEB-INF/spring/*.xml

       </param-value>

    </context-param>

    <context-param>

       <param-name>contextClass</param-name>

        <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext

       </param-value>

    </context-param>

    <listener>

       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

    </listener>

    <servlet>

       <servlet-name>CXFServlet</servlet-name>

       <servlet-class>com.boco.gutil.webservice.context.CXFWrapperServlet</servlet-class>

       <load-on-startup>0</load-on-startup>

    </servlet>

 

    <servlet-mapping>

       <servlet-name>CXFServlet</servlet-name>

       <url-pattern>/*</url-pattern>

    </servlet-mapping>

</web-app>

4.5.8   MANIFEST.MF

Manifest-Version: 1.0

Bundle-ManifestVersion: 2

Bundle-Name: Webservice

Bundle-SymbolicName: com.boco.gutil.webservice

Bundle-Version: 1.0.0.qualifier

Bundle-Vendor: BOCO

Web-ContextPath: GDPP_WS

Bundle-RequiredExecutionEnvironment: JavaSE-1.6

Export-Package: com.boco.gutil.webservice.exports,

 org.apache.cxf.frontend,

 org.apache.cxf.jaxws

Bundle-ClassPath: .,

 lib/cxf-2.3.1.jar,

 lib/cxf-wstx-msv-validation-2.3.1.jar,

 lib/cxf-xjc-boolean-2.3.0.jar,

 lib/cxf-xjc-bug671-2.3.0.jar,

 lib/cxf-xjc-dv-2.3.0.jar,

 lib/cxf-xjc-ts-2.3.0.jar,

 lib/msv-20050913.jar,

 lib/neethi-2.0.4.jar,

 lib/oro-2.0.8.jar,

 lib/stax2-api-3.0.2.jar,

 lib/velocity-1.6.4.jar,

 lib/woodstox-core-asl-4.0.8.jar,

 lib/wsdl4j-1.6.2.jar,

 lib/xmlbeans-2.4.0.jar,

 lib/XmlSchema-1.4.7.jar,

 lib/xmlsec-1.4.4.jar

Import-Package: javax.servlet,

 javax.servlet.http;version="2.5.0",

 javax.xml.ws,

 org.osgi.framework,

 org.osgi.util.tracker,

 org.springframework.beans;version="3.0.6.RELEASE",

 org.springframework.beans.factory;version="3.0.6.RELEASE",

 org.springframework.beans.factory.config;version="3.0.6.RELEASE",

 org.springframework.beans.factory.support;version="3.0.6.RELEASE",

 org.springframework.beans.factory.wiring;version="3.0.6.RELEASE",

 org.springframework.beans.factory.xml;version="3.0.6.RELEASE",

 org.springframework.context;version="3.0.6.RELEASE",

 org.springframework.context.event;version="3.0.6.RELEASE",

 org.springframework.context.support;version="3.0.6.RELEASE",

 org.springframework.core;version="3.0.6.RELEASE",

 org.springframework.core.io;version="3.0.6.RELEASE",

 org.springframework.core.io.support;version="3.0.6.RELEASE",

 org.springframework.osgi.context;version="2.0.0.M1",

 org.springframework.osgi.web.context.support;version="2.0.0.M1",

 org.springframework.web.context;version="3.0.6.RELEASE"

Bundle-ActivationPolicy: lazy

Bundle-Activator: com.boco.gutil.webservice.context.WsActivator

其中:

Web-ContextPath: GDPP_WS

设置webserviceweb 上下文路径。

Bundle-Activator: com.boco.gutil.webservice.context.WsActivator

设置ws-util osgi启动时触发器。

5       发布WebService(依赖ws-utilbundle

5.1   创建ServiceTracker

public class WSRegisterTracker extends ServiceTracker {

    public WSRegisterTracker(BundleContext context) {

       super(context,WSRegister.class.getName(),null);

    }

   

    public Object addingService(ServiceReference reference) {

       Object serviceObj = context.getService(reference);

       //注册webservice

       WSRegister wsRegister = (WSRegister)serviceObj;

       wsRegister.regiser(new HelloWorldImpl(), "/Hello");

      

       return super.addingService(reference);

    }

}

 

5.2   创建Activator

public class WSClientActivator implements BundleActivator {

    private WSRegisterTracker tracker = null;

    public void start(BundleContext context) throws Exception {

       tracker = new WSRegisterTracker(context);

       tracker.open();

    }

 

    public void stop(BundleContext bundleContext) throws Exception {

       tracker.close();

    }

}

5.3   接口,实现类和引用类

@WebService

public interface IHelloWorld {

 

    String sayHi(String text);

 

    String sayHiToUser(User user);

 

}

 

@WebService

public class HelloWorldImpl implements IHelloWorld {

    public String sayHi(String text) {

        System.out.println("sayHi called");

        return "Hello " + text;

    }

 

    public String sayHiToUser(User user) {

        System.out.println("sayHiToUser called");

        return "Hello "  + user.getName();

    }

}

 

public class User implements Serializable{

    String name;

 

    public User() {

    }

    public User(String s) {

        name = s;

    }

 

    public String getName() {

        return name;

    }

 

    public void setName(String s) {

        name = s;

    }

}