项目应用Spring DM实现模块化开发。系统和其他进程之间的控制信息希望能通过Webservice实现。要求如下:
Ø 动态发布
有多个Bundle需要发布WebService,所以希望创建一个Webservice Util(下面简称ws-util)的Bundle并提供易用的接口发布为OSGI的服务,其它Bundle可以通过调用该服务即可实现Webservice的发布。
Ø 依赖最小化
需要发布的Bundle依赖ws-util,但不需要依赖CXF,如果把CXF替换为其他的Webservice引擎,不会引起其他Bundle的变更。
Ø 编码方便
尽可能减少对业务代码的侵入。
1, 如何实现动态发布?
需要发布的Bundle依赖ws-util,调用接口即可完成发布,所以不能在ws-util中静态配置webservice。
解决的办法是利用jax-ws 中定义EndPoint接口,对该接口包装成一个类并发布为OSGI Service,其他bundle调用该service完成webservice的发布。
2, ws-util的上下文环境是什么样的?
Project中存在WEB-INF目录,并且存在web.xml描述文件,osgi的tomcat bundle启动时会自动监视该目录并且加载该文件,并把ws-util作为war包部署到web容器中。
OSGI CORE启动时会监视METAINF.MF中的MANIFEST.MF文件,并初始化OSGI上下文,发布OSGI Service。所以ws-util处于双重的容器管理之下,一重是OSGI容器管理,一重是WEB容器管理。
Tomcat bundle也是由OSGI CORE启动,所以ws-util的OSGI上下文初始化要早于Web上下文的初始化,如果用META-INF/spring下的spring配置方式发布EndPoint包装接口,此时的CXF Bus还没有开始初始化,EndPoint找不到web server的provider,发布就不会成功。
3, 如何把tomcat和cxf整合起来?
1)注册Activator, 在activator初始化方法中获取osgi上下文。
2)web上下文加载CxfNoSpringServlet时,初始化cxf bus后,获取缓存中的osgi上下文,并且把EndPoint包装类注册为osgi 服务。
JDK:1.6
Eclipse:3.5.2
Spring:3.0.6
CXF:2.3.1
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/ 网站下载。
目录说明如下:
SRC |
插件代码 |
Lib |
CXF 依赖包 |
META-INF |
OSGI工程的配置项 |
WEB-INF |
当前项目要发布到Web容器中,OSGI根据该目录的Web.xml确定部署方式 |
Webservice lib下面的jar都是来自于cxf,但是我们用不了所有的jar包,需要根据我们的需要剔除一部分。
我们需要把webservice发布到Tomcat中,并且以jax-ws的方式,可以从cxf的lib中剔除jetty和restful实现相关的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 |
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" |
导入结束后,会在project的plug-in Dependencies下出现下列bundle:
|
说明:
WSRegister |
EndPoint包装类接口 |
WSRegisterImpl |
包装接口实现类 |
CXFWrapperServlet |
CXFNoSpringServlet |
WsActivator |
OSGI启动类 |
ContextStore |
OSGI 上下文缓存 |
package com.boco.gutil.webservice.exports;
public interface WSRegister { public void regiser(Object impl,String address); } |
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); } } |
public class ContextStore { private static BundleContext context = null;
public static BundleContext getContext() { return context; }
public static void setContext(BundleContext context) { ContextStore.context = context; } } |
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); }
} |
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"); } } |
<?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> |
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
设置webservice的web 上下文路径。
Bundle-Activator: com.boco.gutil.webservice.context.WsActivator
设置ws-util osgi启动时触发器。
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); } } |
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(); } } |
@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; } } |