您的位置:首页 > 运维架构 > Tomcat

扩展Tomcat支持OSGi应用服务

2009-11-11 19:11 267 查看

1.摘要

OSGi的动态服务,包版本管理,安全设施,热插拔等特性吸引了越来越多开发人员的关注,由于OSGi不同于以往的设计理念,在企业级应用中一直没有很大的发挥。不过通过大家的不断努力,OSGi已在企业级服务平台中有所集成。本文站在另一个角度尝试Tomcat服务器下的OSGi服务集成,为web应用提供动态服务,为企业级开发提供参考。
本文需要读者了解以下内容:
lJava编程及一些简单的设计模式
lOSGi的相关知识
lTomcat服务器的相关知识

2.集成思路

我们知道OSGiR4.0平台中发布的服务为java对象服务,只能在其内部使用,而不能够为外部环境所使用的主要原因是类加载问题。要想为外部环境引用OSGi平台中的服务,我们需要使用java的反射机制来达到目的。
鉴于OSGi平台服务的各种新的让人向往的优势,我们当然希望将其应用于开发和生产环境中。那么如何将OSGi平台服务集成到Tomcat中,并被web应用所使用呢?首先我们需要了解一些基本知识。

2.1J2EE基础知识

我们知道,J2EE平台的JNDI规范,规定了服务器如何提供JNDI服务,同时也规定了web应用如何使用JNDI服务。举个例子,我们经常在服务器中配置数据库连接池,然后在web应用中使用(不明白的可以参考《Tomcat中数据源的配置及原理》)DataSource对象,而这个DataSource对象就是通过JNDI发布给web应用的。
当然,JNDI不仅仅能够发布DataSource服务对象,它还可以发布其它java服务对象。基于这个原理,我们很自然地会想,能否将OSGi平台的服务对象发布到JNDI呢?答案是肯定的。把OSGi平台的服务对象发布到JNDI的方式有很多种,这里介绍通过扩展Tomcat服务器来集成OSGi平台来实现以上的想法。

2.2扩展Tomcat,支持OSGi平台

这里我们选择支持以下OSGi平台和Tomcat服务器:
1.Eclipse的OSGi平台实现equinox,版本equinox3.3.2。
2.Apache的OSGi平台实现felix,版本felix1.6.0。
3.Apache的Web应用服务器Tomcat,版本tomcat6.0.18(6.0以上目录结构一致)。
我们选择扩展Tomcat服务器的目的是,希望保持OSGi环境对Tomcat的透明,即OSGi平台的集成不会影响Tomcat服务器的功能。我们需要在Tomcat启动时启动OSGi平台,在Tomcat停止时停止OSGi平台。所以我们将服务器的启动作为扩展点,具体来说,就是在Tomcat的server组件启动时,添加一个关于OSGi平台生命周期的监听器(OsgiLifecycleListener),OsgiLifecycleListener必须实现LifecycleListener接口,这样就可以接受到server组件的事件通知了,根据不同事件,就可以在Tomcat的server组件启动时,启动OSGi平台,在server组件停止时,停止OSGi平台了。

2.3Tomcat中发布JNDI服务

集成了OSGi平台后,我们希望将OSGi平台中服务发布到JNDI中,而OSGi平台中的服务对象各不相同,且是动态的,如果直接将这些服务发布给JNDI,web的使用将是痛苦的,也将不能获得OSGi动态服务的特性。所以,我们需要抽象一个共同的服务接口(OsgiServices),将该接口发布到JNDI中,而当web应用使用OSGi服务时,通过该接口来查找相应的服务。
为了在Tomcat中发布JNDI资源(OsgiServices),我们创建自己的资源工厂OsgiServicesFactory,OsgiServicesFactory实现了对象工厂(ObjectFactory)。

2.4集成模型

到目前为止,我们的集成模型可以用图表示如下:




3.实现步骤

有了以上的分析和设计思路,我们接下来详细描述实现过程及需要注意的地方。

3.1扩展Tomcat,集成OSGi平台

Step1.创建java项目com.dinstone.tomcat.osgi,创建OsgiLifecycleListener类:

packagecom.dinstone.tomcat.osgi;

importjava.util.logging.Logger;

importorg.apache.catalina.Lifecycle;
importorg.apache.catalina.LifecycleEvent;
importorg.apache.catalina.LifecycleListener;

importcom.dinstone.osgi.OsgiServices;

publicclassOsgiLifecycleListenerimplementsLifecycleListener{

privatestaticLoggerlog=Logger.getLogger(OsgiLifecycleListener.class
.getName());

privatestaticOsgiLifecycleListenerlistener=null;

/**theosgiTypedefaultvalueis'Equixox'.*/
privateStringosgiType="Equinox";

privateOsgiContentosgiContent=null;

publicOsgiLifecycleListener(){
}

publicstaticOsgiLifecycleListenergetInstance(){
returnlistener;
}

@Override
publicvoidlifecycleEvent(LifecycleEventevent){
if(Lifecycle.INIT_EVENT.equals(event.getType())){
log.info("Theosgicontentisinitialized.Usingosgicontent:"
+osgiType);
try{
initContent();
}catch(Exceptione){
e.printStackTrace();
}
}elseif(Lifecycle.START_EVENT.equals(event.getType())){
try{
log.info("Startingosgiservice.");
osgiContent.start();
}catch(Exceptione){
e.printStackTrace();
log.info("Startingtheosgicontentoccurederror."
+e.getMessage());
}
}elseif(Lifecycle.STOP_EVENT.equals(event.getType())){
try{
log.info("Stoppingosgiservice.");
osgiContent.stop();
}catch(Exceptione){
e.printStackTrace();
log.info("Stoppingtheosgicontentoccurederror."
+e.getMessage());
}
}
}

privatevoidinitContent()throwsException{
listener=this;
osgiContent=OsgiContentFactory.getInstance().getOsgiContent(osgiType);
}

publicStringgetOsgiType(){
returnosgiType;
}

publicvoidsetOsgiType(StringosgiType){
this.osgiType=osgiType;
}

publicOsgiServicesgetOsgiServices(){
returnosgiContent;
}

publicvoidsetOsgiContent(OsgiContentosgiContent){
this.osgiContent=osgiContent;
}
}

Step2.打开${Tomcat_Home}/conf/server.xml.。${Tomcat_Home}为Tomcat安装目录,下同。
添加红色部分:

<Serverport="8005"shutdown="SHUTDOWN">
<!--APRlibraryloader.Documentationat/docs/apr.html-->
<ListenerclassName="org.apache.catalina.core.AprLifecycleListener"SSLEngine="on"/>
<!--InitializeJasperpriortowebappsareloaded.Documentationat/docs/jasper-howto.html-->
<ListenerclassName="org.apache.catalina.core.JasperListener"/>
<!--JMXSupportfortheTomcatserver.Documentationat/docs/non-existent.html-->
<ListenerclassName="org.apache.catalina.mbeans.ServerLifecycleListener"/>
<ListenerclassName="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
<!--OSGisupportfortheTomcatserver-->
<ListenerclassName="com.dinstone.tomcat.osgi.OsgiLifecycleListener"osgiType="felix"/>


Step3.打开${Tomcat_Home}/conf/catalina.properties。修改红色部分:

#
#
#Listofcomma-separatedpathsdefiningthecontentsofthe"common"
#classloader.Prefixesshouldbeusedtodefinewhatistherepositorytype.
#PathmayberelativetotheCATALINA_HOMEorCATALINA_BASEpathorabsolute.
#Ifleftasblank,theJVMsystemloaderwillbeusedasCatalina's"common"
#loader.
#Examples:
#"foo":Addthisfolderasaclassrepository
#"foo/*.jar":AddalltheJARsofthespecifiedfolderasclass
#repositories
#"foo/bar.jar":Addbar.jarasaclassrepository
#${catalina.home}/osgi/equinox/plugins,${catalina.home}/osgi/equinox/plugins/*.jar,
common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar,${catalina.home}/osgi/felix/bin/*.jar
Step4.构建equinox环境。
新建目录:${Tomcat_Home}/osgi/equinox/plugins/,将org.eclipse.osgi_3.3.2.R33x_v20080105.jar放于该目录下。
Step5.构建felix环境。
新建目录:${Tomcat_Home}/osgi/felix/,将下载的felix-1.6.0.zip解压到该目录。最终的目录结构如图:




Step5.创建服务接口:

packagecom.dinstone.osgi;

publicinterfaceOsgiServices{

publicObjectgetOSGiService(StringserviceName);

publicClass<?>getBundleClass(StringbundleName,StringclassName)
throwsClassNotFoundException;
}

3.2发布OSGi服务到JNDI

Step6.创建资源工厂类:

packagecom.dinstone.tomcat.osgi;

importjava.util.Enumeration;
importjava.util.Hashtable;
importjava.util.logging.Logger;

importjavax.naming.Context;
importjavax.naming.Name;
importjavax.naming.RefAddr;
importjavax.naming.Reference;
importjavax.naming.spi.ObjectFactory;

importcom.dinstone.osgi.OsgiServices;

publicclassOsgiServicesFactoryimplementsObjectFactory{

privatestaticLoggerlog=Logger.getLogger(OsgiServicesFactory.class
.getName());

privateOsgiServicesosgiServices;

@Override
publicObjectgetObjectInstance(Objectobj,Namename,ContextnameCtx,
Hashtable<?,?>environment)throwsException{

//Customizethebeanpropertiesfromourattributes
Referenceref=(Reference)obj;
Enumeration<RefAddr>addrs=ref.getAll();
while(addrs.hasMoreElements()){
RefAddraddr=addrs.nextElement();
StringattrName=addr.getType();
Stringvalue=(String)addr.getContent();
log.info("theattributeis("+attrName+"=="+value);
}

initContext();
returnosgiServices;
}

privatevoidinitContext(){
if(osgiServices==null){
OsgiLifecycleListenerosgilcl=OsgiLifecycleListener.getInstance();
osgiServices=osgilcl.getOsgiServices();
}
}

}
Step7.打开${Tomcat_Home}/conf/context.xml。添加以下内容:

<Resourcename="osgi/services"auth="Container"
type="com.dinstone.osgi.OsgiServices"
factory="com.dinstone.tomcat.osgi.OsgiServicesFactory"/>
说明:
1.OsgiLifecycleListener为单例对象,主要功能为根据配置信息osgiType来加载不同的OSGi平台,根据事件类型来启动和停止OSGi平台。
2.osgiType必须有get/set方法,Tomcat会注入配置信息。
3.将com.dinstone.tomcat.osgi工程编译打包成com.dinstone.tomcat.osgi_1.12.4.jar,将jar放于${Tomcat_Home}/lib目录下。
4.step7中的配置方式意味着所有的应用都可以引用"osgi/services"资源。另外一种方式可以在web应用的发布文件中配置,具体参见其它相关文档。

3.3Web应用引用JNDI资源

Web应用为了引用JNDI资源,需要使用java的反射机制来调用资源服务。首先我们建立web端得JNDI资源应用API。
Step1.创建java项目com.dinsotne.web.osgi,创建JndiOsgiServicesFactory类,负责在JNDI中查找OsgiServices服务。

packagecom.dinsotne.web.osgi;

importjavax.naming.Context;
importjavax.naming.InitialContext;
importjavax.naming.NamingException;

importcom.dinstone.osgi.OsgiServices;

publicclassJndiOsgiServicesFactoryimplementsOsgiServicesFactory{

/**JNDIprefixusedinaJ2EEcontainer*/
privatestaticfinalStringCONTAINER_PREFIX="java:comp/env/";

privateStringjndiName;

publicStringgetJndiName(){
returnjndiName;
}

publicvoidsetJndiName(StringjndiName){
this.jndiName=jndiName;
}

publicOsgiServicesgetOsgiServices(){
return(OsgiServices)lookup(getJndiName());
}

privateObjectlookup(StringjndiName){
StringconvertedName=convertJndiName(jndiName);
ObjectjndiObject=null;
try{
Contextcontext=newInitialContext();
jndiObject=context.lookup(convertedName);
}catch(NamingExceptione){
thrownewIllegalServiceException(
"TheJNDIOSGiservicesnameiserror.",e);
}catch(Exceptione){
thrownewIllegalServiceException(
"TheJNDIOSGiservicescannotbeinitialed.",e);
}

returnjndiObject;
}

privateStringconvertJndiName(StringjndiName){
if(!jndiName.startsWith(CONTAINER_PREFIX)
&&jndiName.indexOf(':')==-1){
jndiName=CONTAINER_PREFIX+jndiName;
}
returnjndiName;
}
}
Step2.在web应用的web.xml中添加如下内容:

<resource-env-ref>
<description>osgiservices</description>
<resource-env-ref-name>osgi/services</resource-env-ref-name>
<resource-env-ref-type>
com.dinstone.osgi.OsgiServices
</resource-env-ref-type>
</resource-env-ref>

3.4Web应用调用OSGi服务

Step3.有了OsgiServices服务后,我们创建OsgiServiceFactory类,负责获取OSGi平台的动态服务。

packagecom.dinsotne.web.osgi;

importcom.dinstone.osgi.OsgiServices;

publicclassOsgiServiceFactory{

privateOsgiServicesservices;

publicOsgiServiceFactory(OsgiServicesservices){
this.services=services;
}

publicOsgiServiceFactory(){
}

public<T>TgetOsgiService(Class<T>serviceType,StringserviceName){
OsgiServiceInvocationHandlerhandler=newOsgiServiceInvocationHandler(
services,serviceName);
returnJavaProxyObjectFactory.getProxyObject(serviceType,handler);
}

publicOsgiServicesgetServices(){
returnservices;
}

publicvoidsetServices(OsgiServicesservices){
this.services=services;
}

}
Step4.为了方便Web端得调用,我们创建了类OsgiServiceFacade。

packagecom.dinsotne.web.osgi;

importcom.dinstone.osgi.OsgiServices;

publicclassOsgiServiceFacade{

publicstatic<T>TgetOsgiService(StringjndiName,Class<T>serviceType,
StringserviceName){
JndiOsgiServicesFactoryfactory=newJndiOsgiServicesFactory();
factory.setJndiName(jndiName);

OsgiServicesservices=factory.getOsgiServices();
OsgiServiceFactorysf=newOsgiServiceFactory(services);
returnsf.getOsgiService(serviceType,serviceName);
}
}
Step5.Web调用示例。

publicstaticStringgetUserName(Stringid){
try{
IUserServiceservice=OsgiServiceFacade.getOsgiService(
"osgi/services",IUserService.class,IUserService.class
.getName());

returnservice.getUserName(id);
}catch(IllegalArgumentExceptione){
e.printStackTrace();
e.printStackTrace();
}

returnnull;
}
说明:
1.以上的代码应用了java代理和反射技术,其它的代码参见源码。
2.OsgiServiceFactory在获取OSGi平台服务时,使用了java代理。读者可能会疑问,为什么Datasource资源服务的引用就不必使用反射,而我们的OSGi服务就需要使用反射啊?这个都是java的类加载机制惹得祸。对于Datasource资源,它的类型是javax.sql.DataSource,为系统类,且运行在Tomcat中的web应用都使用Tomcat容器的类加载器加载这个类,故web应用中的javax.sql.DataSource跟Tomcat加载的是同一类。但是,对于OSGi服务类,该类由OSGi容器的类加载器加载,而我们的web应用是不能使用该类加载器加载该类的,故只能通过反射来调用服务了。
3.将项目工程com.dinsotne.web.osgi导出打包:com.dinsotne.web.osgi_1.12.0.jar。

4.测试

通过以上的实现,我们将OSGi平台集成到了Tomcat中,并且明确了web应用如何使用OSGi服务。下面就来创建一个测试的例子,看看OSGi编程模式对web应用开发的影响,同时也测试一下集成的效果。

4.1创建并发布OSGi服务

Step1.创建插件工程com.dinstone.demo.user,选择standardOSGiframework。创建接口:

packagecom.dinstone.demo.user;

publicinterfaceIUserService{
publicStringgetUserName(Stringid);
}
Step2.创建插件工程com.dinstone.demo.user.db,创建类UserServiceImpl实现IUserService接口。

packagecom.dinstone.demo.user.db;
importjava.util.logging.Logger;
importcom.dinstone.demo.user.IUserService;

publicclassUserServiceImplimplementsIUserService{

privatestaticLoggerlog=Logger.getLogger(UserServiceImpl.class
.getName());

@Override
publicStringgetUserName(Stringid){
log.info("getusernamefromdb");
return"db"+id;
}
}
Step3.向OSGi平台发布IUserService服务。创建Activator注册服务对象。

packagecom.dinstone.demo.user.db;

importjava.util.Properties;

importorg.osgi.framework.BundleActivator;
importorg.osgi.framework.BundleContext;
importorg.osgi.framework.ServiceRegistration;

importcom.dinstone.demo.user.IUserService;

publicclassActivatorimplementsBundleActivator{

privateServiceRegistrationserviceReg;

/*
*(non-Javadoc)
*
*@seeorg.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
*/
publicvoidstart(BundleContextcontext)throwsException{
Propertiesp=newProperties();
serviceReg=context.registerService(IUserService.class.getName(),
newUserServiceImpl(),p);
}

/*
*(non-Javadoc)
*
*@seeorg.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
publicvoidstop(BundleContextcontext)throwsException{
serviceReg.unregister();
}

}
Step4.创建插件工程com.dinstone.demo.user.file,创建类UserServiceImpl实现IUserService接口。

packagecom.dinstone.demo.user.file;

importjava.util.logging.Logger;

importcom.dinstone.demo.user.IUserService;

publicclassUserServiceImplimplementsIUserService{

privatestaticLoggerlog=Logger.getLogger(UserServiceImpl.class
.getName());

@Override
publicStringgetUserName(Stringid){
log.info("getusernamefromfile");
return"file"+id;
}
}
Step5.向OSGi平台发布IUserService服务。创建Activator注册服务对象。

packagecom.dinstone.demo.user.file;

importjava.util.Properties;

importorg.osgi.framework.BundleActivator;
importorg.osgi.framework.BundleContext;
importorg.osgi.framework.ServiceRegistration;

importcom.dinstone.demo.user.IUserService;

publicclassActivatorimplementsBundleActivator{

privateServiceRegistrationserviceReg;

/*
*(non-Javadoc)
*
*@seeorg.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
*/
publicvoidstart(BundleContextcontext)throwsException{
Propertiesp=newProperties();
serviceReg=context.registerService(IUserService.class.getName(),
newUserServiceImpl(),p);
}

/*
*(non-Javadoc)
*
*@seeorg.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
publicvoidstop(BundleContextcontext)throwsException{
serviceReg.unregister();
}
}

4.2创建Web应用

Step1.创建动态web工程webDemo,并新建类:UserServiceFacade。

packagecom.dinsotne.web.demo;

importcom.dinsotne.web.osgi.OsgiServiceFacade;
importcom.dinstone.demo.user.IUserService;

publicclassUserServiceFacade{

publicstaticStringgetUserName(Stringid){
try{
IUserServiceservice=OsgiServiceFacade.getOsgiService(
"osgi/services",IUserService.class,IUserService.class
.getName());

returnservice.getUserName(id);
}catch(IllegalArgumentExceptione){
e.printStackTrace();
e.printStackTrace();
}

returnnull;
}
}
Step2.创建index.jsp页面。

<%@pagelanguage="java"contentType="text/html;charset=utf-8"
pageEncoding="ISO-8859-1"%>
<!DOCTYPEhtmlPUBLIC"-//W3C//DTDHTML4.01Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<%@pageimport="com.dinsotne.web.demo.UserServiceFacade"%>
<html>
<head>
<metahttp-equiv="Content-Type"content="text/html;charset=utf-8">
<title>Inserttitlehere</title>
</head>
<body>
UserNameis
<%=UserServiceFacade.getUserName("001")%>

</body>
</html>
Step3.修改web.xml文件,添加红色部分。

<?xmlversion="1.0"encoding="UTF-8"?>
<web-appxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"id="WebApp_ID"version="2.5">
<display-name>webDemo</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<resource-env-ref>
<description>osgiservice</description>
<resource-env-ref-name>osgi/services</resource-env-ref-name>
<resource-env-ref-type>
com.dinstone.osgi.OsgiServices
</resource-env-ref-type>
</resource-env-ref>
</web-app>
说明:
1.由于UserServiceFacade依赖IUserService类,故需要将com.dinstone.demo.user_1.0.0.jar(参见4.3)添加到lib中。
2.由于UserServiceFacade依赖OsgiServiceFacade类,故将com.dinsotne.web.osgi_1.12.0.jar(参见3.4说明)添加到lib中。

4.3发布OSGiBundle

Step1.依次从插件工程导出插件包:com.dinstone.demo.user_1.0.0.jar,com.dinstone.demo.user.file_1.0.0.jar,com.dinstone.demo.user.db_1.0.0.jar。
Step2.将以上的插件放于${Tomcat_Home}/osgi/felix/bundle目录下。

4.4发布web应用

Step1.导出web应用webDemo.war。
Step2.将webDemo.war放于${Tomcat_Home}/webapps目录下。

4.5启动Tomcat并安装OSGiBundle

Step1.在命令行下启动Tomcat。E:/Cluster/apache-tomcat-6.0.18为我的${Tomcat_Home}。

E:/Cluster/apache-tomcat-6.0.18>bin/startup.bat
UsingCATALINA_BASE:E:/Cluster/apache-tomcat-6.0.18
UsingCATALINA_HOME:E:/Cluster/apache-tomcat-6.0.18
UsingCATALINA_TMPDIR:E:/Cluster/apache-tomcat-6.0.18/temp
UsingJRE_HOME:C:/ProgramFiles/Java/jdk1.6.0_10
2009-8-1213:21:39com.dinstone.tomcat.osgi.OsgiLifecycleListenerlifecycleEvent
信息:Theosgicontentisinitialized.Usingosgicontent:felix
2009-8-1213:21:39org.apache.coyote.http11.Http11Protocolinit
信息:InitializingCoyoteHTTP/1.1onhttp-8080
2009-8-1213:21:40org.apache.coyote.http11.Http11Protocolinit
信息:InitializingCoyoteHTTP/1.1onhttp-8443
2009-8-1213:21:40org.apache.catalina.startup.Catalinaload
信息:Initializationprocessedin1748ms
2009-8-1213:21:41com.dinstone.tomcat.osgi.OsgiLifecycleListenerlifecycleEvent
信息:Startingosgiservice.
2009-8-1213:21:41com.dinstone.tomcat.osgi.felix.FelixContentstart
信息:*********************************
2009-8-1213:21:41com.dinstone.tomcat.osgi.felix.FelixContentstart
信息:catalinahomeisE:/Cluster/apache-tomcat-6.0.18
2009-8-1213:21:41com.dinstone.tomcat.osgi.felix.FelixContentstart
信息:osgihomeisE:/Cluster/apache-tomcat-6.0.18/osgi/felix
2009-8-1213:21:41com.dinstone.tomcat.osgi.felix.FelixContentstart
信息:******user.dirisE:/Cluster/apache-tomcat-6.0.18

WelcometoFelix.
=================

->2009-8-1213:21:42org.apache.catalina.core.StandardServicestart
信息:StartingserviceCatalina
2009-8-1213:21:42org.apache.catalina.core.StandardEnginestart
信息:StartingServletEngine:ApacheTomcat/6.0.18
2009-8-1213:21:42org.apache.catalina.loader.WebappLoaderstart
信息:Dualregistrationofjndistreamhandler:factoryalreadydefined
2009-8-1213:21:44org.apache.coyote.http11.Http11Protocolstart
信息:StartingCoyoteHTTP/1.1onhttp-8080
2009-8-1213:21:44org.apache.coyote.http11.Http11Protocolstart
信息:StartingCoyoteHTTP/1.1onhttp-8443
2009-8-1213:21:44org.apache.jk.common.ChannelSocketinit
信息:JK:ajp13listeningon/0.0.0.0:8009
2009-8-1213:21:44org.apache.jk.server.JkMainstart
信息:JkrunningID=0time=0/47config=null
2009-8-1213:21:44org.apache.catalina.startup.Catalinastart
信息:Serverstartupin3882ms
->ps
STARTLEVEL1
IDStateLevelName
[0][Active][0]SystemBundle(1.6.0)
[25][Active][1]ApacheFelixShellService(1.2.0)
[26][Active][1]ApacheFelixShellTUI(1.2.0)
[27][Active][1]ApacheFelixBundleRepository(1.4.0)
->
Step2.安装bundle。

->installfile:E:/Cluster/apache-tomcat-6.0.18/osgi/felix/bundle/com.dinstone.demo.user_1.0.0.jar
BundleID:39
->installfile:E:/Cluster/apache-tomcat-6.0.18/osgi/felix/bundle/com.dinstone.demo.user.db_1.0.0.jar
BundleID:40
->installfile:E:/Cluster/apache-tomcat-6.0.18/osgi/felix/bundle/com.dinstone.demo.user.file_1.0.0.jar
BundleID:41
->ps
STARTLEVEL1
IDStateLevelName
[0][Active][0]SystemBundle(1.6.0)
[25][Active][1]ApacheFelixShellService(1.2.0)
[26][Active][1]ApacheFelixShellTUI(1.2.0)
[27][Active][1]ApacheFelixBundleRepository(1.4.0)
[39][Installed][1]UserModelInterfacePlug-in(1.0.0)
[40][Installed][1]UserDBImplementPlug-in(1.0.0)
[41][Installed][1]UserFileImplementPlug-in(1.0.0)
->
Step3.访问web应用,http://localhost:8080/webDemo/。由于没有启动OSGi服务,故出现500异常页面,错误原因是没有找到服务。

rootcause

com.dinsotne.web.osgi.IllegalServiceException:Cann'tfindoutosgiservice:com.dinstone.demo.user.IUserService

com.dinsotne.web.osgi.OsgiServiceInvocationHandler.invoke(OsgiServiceInvocationHandler.java:30)

$Proxy0.getUserName(UnknownSource)

com.dinsotne.web.demo.UserServiceFacade.getUserName(UserServiceFacade.java:14)

org.apache.jsp.index_jsp._jspService(index_jsp.java:64)

org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)

javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:374)

org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:342)

org.apache.jasper.servlet.JspServlet.service(JspServlet.java:267)

javax.servlet.http.HttpServlet.service(HttpServlet.java:717)


Step4.启动UserDBImplementPlug-in服务,激活User模块的DB实现。

->ps
STARTLEVEL1
IDStateLevelName
[0][Active][0]SystemBundle(1.6.0)
[25][Active][1]ApacheFelixShellService(1.2.0)
[26][Active][1]ApacheFelixShellTUI(1.2.0)
[27][Active][1]ApacheFelixBundleRepository(1.4.0)
[39][Installed][1]UserModelInterfacePlug-in(1.0.0)
[40][Installed][1]UserDBImplementPlug-in(1.0.0)
[41][Installed][1]UserFileImplementPlug-in(1.0.0)
->start40
->ps-s
STARTLEVEL1
IDStateLevelSymbolicname
[0][Active][0]org.apache.felix.framework(1.6.0)
[25][Active][1]org.apache.felix.shell(1.2.0)
[26][Active][1]org.apache.felix.shell.tui(1.2.0)
[27][Active][1]org.apache.felix.bundlerepository(1.4.0)
[39][Resolved][1]com.dinstone.demo.user(1.0.0)
[40][Active][1]com.dinstone.demo.user.db(1.0.0)
[41][Installed][1]com.dinstone.demo.user.file(1.0.0)
->
访问http://localhost:8080/webDemo/。页面显示:





Step5.启动UserFileImplementPlug-in服务,激活User模块的File实现。

->ps
STARTLEVEL1
IDStateLevelName
[0][Active][0]SystemBundle(1.6.0)
[25][Active][1]ApacheFelixShellService(1.2.0)
[26][Active][1]ApacheFelixShellTUI(1.2.0)
[27][Active][1]ApacheFelixBundleRepository(1.4.0)
[39][Resolved][1]UserModelInterfacePlug-in(1.0.0)
[40][Active][1]UserDBImplementPlug-in(1.0.0)
[41][Installed][1]UserFileImplementPlug-in(1.0.0)
->start41
->ps
STARTLEVEL1
IDStateLevelName
[0][Active][0]SystemBundle(1.6.0)
[25][Active][1]ApacheFelixShellService(1.2.0)
[26][Active][1]ApacheFelixShellTUI(1.2.0)
[27][Active][1]ApacheFelixBundleRepository(1.4.0)
[39][Resolved][1]UserModelInterfacePlug-in(1.0.0)
[40][Active][1]UserDBImplementPlug-in(1.0.0)
[41][Active][1]UserFileImplementPlug-in(1.0.0)
->
访问http://localhost:8080/webDemo/。页面显示:



Step6.现在停止UserDBImplementPlug-in服务。

->ps
STARTLEVEL1
IDStateLevelName
[0][Active][0]SystemBundle(1.6.0)
[25][Active][1]ApacheFelixShellService(1.2.0)
[26][Active][1]ApacheFelixShellTUI(1.2.0)
[27][Active][1]ApacheFelixBundleRepository(1.4.0)
[39][Resolved][1]UserModelInterfacePlug-in(1.0.0)
[40][Active][1]UserDBImplementPlug-in(1.0.0)
[41][Active][1]UserFileImplementPlug-in(1.0.0)
->stop40
->ps
STARTLEVEL1
IDStateLevelName
[0][Active][0]SystemBundle(1.6.0)
[25][Active][1]ApacheFelixShellService(1.2.0)
[26][Active][1]ApacheFelixShellTUI(1.2.0)
[27][Active][1]ApacheFelixBundleRepository(1.4.0)
[39][Resolved][1]UserModelInterfacePlug-in(1.0.0)
[40][Resolved][1]UserDBImplementPlug-in(1.0.0)
[41][Active][1]UserFileImplementPlug-in(1.0.0)
->
访问http://localhost:8080/webDemo/。页面显示:





4.6停止Tomcat服务器

重新打开一个命令行窗口,切换到${Tomcat_Home}/bin目录下。执行:

E:/Cluster/apache-tomcat-6.0.18/bin>shutdown.bat
Tomcat服务器关闭。

5结论

通过以上的测试,我们发现以上的实现基本符合最初的设想:
lOSGi的集成对Tomcat几乎是透明的。
lOSGi的所有优点。
lWeb表现和业务逻辑的完全分离。
l基于模块化服务的编程模型。

同时,我们也发现了一些问题:
lWeb层没有支持模块化、可热插拔的编程模型。
lOSGi层的服务日志跟web层的日志分离增加了维护的难度。
l该集成方式没有经严格测试,虽然已经有产品应用了。

附录:

1.测试Demo


2.源码工程

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: