1.摘要
OSGi的動態服務,包版本管理,安全設施,熱插拔等特性吸引了越來越多開發人員的關注,由於OSGi不同於以往的設計理念,在企業級應用中一直沒有很大的發揮。不過通過大家的不斷努力,OSGi已在企業級服務平臺中有所集成。本文站在另一個角度嘗試Tomcat服務器下的OSGi服務集成,爲web應用提供動態服務,爲企業級開發提供參考。
本文需要讀者瞭解以下內容:
l Java編程及一些簡單的設計模式
l OSGi的相關知識
l Tomcat服務器的相關知識
2.集成思路
我們知道OSGi R4.0平臺中發佈的服務爲java對象服務,只能在其內部使用,而不能夠爲外部環境所使用的主要原因是類加載問題。要想爲外部環境引用OSGi平臺中的服務,我們需要使用java的反射機制來達到目的。
鑑於OSGi平臺服務的各種新的讓人嚮往的優勢,我們當然希望將其應用於開發和生產環境中。那麼如何將OSGi平臺服務集成到Tomcat中,並被web應用所使用呢?首先我們需要了解一些基本知識。
2.1 J2EE基礎知識
我們知道,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.3 Tomcat中發佈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類:
package com.dinstone.tomcat.osgi;
import java.util.logging.Logger;
import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleEvent; import org.apache.catalina.LifecycleListener;
import com.dinstone.osgi.OsgiServices;
public class OsgiLifecycleListener implements LifecycleListener {
private static Logger log = Logger.getLogger(OsgiLifecycleListener.class .getName());
private static OsgiLifecycleListener listener = null;
/** the osgiType default value is 'Equixox'. */ private String osgiType = "Equinox";
private OsgiContent osgiContent = null;
public OsgiLifecycleListener() { }
public static OsgiLifecycleListener getInstance() { return listener; }
@Override public void lifecycleEvent(LifecycleEvent event) { if (Lifecycle.INIT_EVENT.equals(event.getType())) { log.info("The osgi content is initialized. Using osgi content:" + osgiType); try { initContent(); } catch (Exception e) { e.printStackTrace(); } } else if (Lifecycle.START_EVENT.equals(event.getType())) { try { log.info("Starting osgi service."); osgiContent.start(); } catch (Exception e) { e.printStackTrace(); log.info("Starting the osgi content occured error. " + e.getMessage()); } } else if (Lifecycle.STOP_EVENT.equals(event.getType())) { try { log.info("Stopping osgi service."); osgiContent.stop(); } catch (Exception e) { e.printStackTrace(); log.info("Stopping the osgi content occured error. " + e.getMessage()); } } }
private void initContent() throws Exception { listener = this; osgiContent = OsgiContentFactory.getInstance().getOsgiContent(osgiType); }
public String getOsgiType() { return osgiType; }
public void setOsgiType(String osgiType) { this.osgiType = osgiType; }
public OsgiServices getOsgiServices() { return osgiContent; }
public void setOsgiContent(OsgiContent osgiContent) { this.osgiContent = osgiContent; } }
|
Step2.打開${Tomcat_Home}/conf/server.xml.。${Tomcat_Home}爲Tomcat安裝目錄,下同。
添加紅色部分:
<Server port="8005" shutdown="SHUTDOWN"> <!--APR library loader. Documentation at /docs/apr.html --> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html --> <Listener className="org.apache.catalina.core.JasperListener" /> <!-- JMX Support for the Tomcat server. Documentation at /docs/non-existent.html --> <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <!-- OSGi support for the Tomcat server --> <Listener className="com.dinstone.tomcat.osgi.OsgiLifecycleListener" osgiType="felix"/> …
|
Step3. 打開${Tomcat_Home}/conf/catalina.properties。修改紅色部分:
# # # List of comma-separated paths defining the contents of the "common" # classloader. Prefixes should be used to define what is the repository type. # Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute. # If left as blank,the JVM system loader will be used as Catalina's "common" # loader. # Examples: # "foo": Add this folder as a class repository # "foo/*.jar": Add all the JARs of the specified folder as class # repositories # "foo/bar.jar": Add bar.jar as a class repository # ${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.創建服務接口:
package com.dinstone.osgi;
public interface OsgiServices {
public Object getOSGiService(String serviceName);
public Class<?> getBundleClass(String bundleName, String className) throws ClassNotFoundException; } |
3.2 發佈OSGi服務到JNDI
Step6.創建資源工廠類:
package com.dinstone.tomcat.osgi;
import java.util.Enumeration; import java.util.Hashtable; import java.util.logging.Logger;
import javax.naming.Context; import javax.naming.Name; import javax.naming.RefAddr; import javax.naming.Reference; import javax.naming.spi.ObjectFactory;
import com.dinstone.osgi.OsgiServices;
public class OsgiServicesFactory implements ObjectFactory {
private static Logger log = Logger.getLogger(OsgiServicesFactory.class .getName());
private OsgiServices osgiServices;
@Override public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
// Customize the bean properties from our attributes Reference ref = (Reference) obj; Enumeration<RefAddr> addrs = ref.getAll(); while (addrs.hasMoreElements()) { RefAddr addr = addrs.nextElement(); String attrName = addr.getType(); String value = (String) addr.getContent(); log.info("the attribute is (" + attrName + " == " + value); }
initContext(); return osgiServices; }
private void initContext() { if (osgiServices == null) { OsgiLifecycleListener osgilcl = OsgiLifecycleListener.getInstance(); osgiServices = osgilcl.getOsgiServices(); } }
} |
Step7.打開${Tomcat_Home}/conf/context.xml。添加以下內容:
<Resource name="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.3 Web應用引用JNDI資源
Web應用爲了引用JNDI資源,需要使用java的反射機制來調用資源服務。首先我們建立web端得JNDI資源應用API。
Step1.創建java項目com.dinsotne.web.osgi,創建JndiOsgiServicesFactory類,負責在JNDI中查找OsgiServices服務。
package com.dinsotne.web.osgi;
import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException;
import com.dinstone.osgi.OsgiServices;
public class JndiOsgiServicesFactory implements OsgiServicesFactory {
/** JNDI prefix used in a J2EE container */ private static final String CONTAINER_PREFIX = "java:comp/env/";
private String jndiName;
public String getJndiName() { return jndiName; }
public void setJndiName(String jndiName) { this.jndiName = jndiName; }
public OsgiServices getOsgiServices() { return (OsgiServices) lookup(getJndiName()); }
private Object lookup(String jndiName) { String convertedName = convertJndiName(jndiName); Object jndiObject = null; try { Context context = new InitialContext(); jndiObject = context.lookup(convertedName); } catch (NamingException e) { throw new IllegalServiceException( "The JNDI OSGi services name is error.", e); } catch (Exception e) { throw new IllegalServiceException( "The JNDI OSGi services can not be initialed.", e); }
return jndiObject; }
private String convertJndiName(String jndiName) { if (!jndiName.startsWith(CONTAINER_PREFIX) && jndiName.indexOf(':') == -1) { jndiName = CONTAINER_PREFIX + jndiName; } return jndiName; } } |
Step2.在web應用的web.xml中添加如下內容:
<resource-env-ref> <description>osgi services</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.4 Web應用調用OSGi服務
Step3.有了OsgiServices服務後,我們創建OsgiServiceFactory類,負責獲取OSGi平臺的動態服務。
package com.dinsotne.web.osgi;
import com.dinstone.osgi.OsgiServices;
public class OsgiServiceFactory {
private OsgiServices services;
public OsgiServiceFactory(OsgiServices services) { this.services = services; }
public OsgiServiceFactory() { }
public <T> T getOsgiService(Class<T> serviceType, String serviceName) { OsgiServiceInvocationHandler handler = new OsgiServiceInvocationHandler( services, serviceName); return JavaProxyObjectFactory.getProxyObject(serviceType, handler); }
public OsgiServices getServices() { return services; }
public void setServices(OsgiServices services) { this.services = services; }
} |
Step4.爲了方便Web端得調用,我們創建了類OsgiServiceFacade。
package com.dinsotne.web.osgi;
import com.dinstone.osgi.OsgiServices;
public class OsgiServiceFacade {
public static <T> T getOsgiService(String jndiName, Class<T> serviceType, String serviceName) { JndiOsgiServicesFactory factory = new JndiOsgiServicesFactory(); factory.setJndiName(jndiName);
OsgiServices services = factory.getOsgiServices(); OsgiServiceFactory sf = new OsgiServiceFactory(services); return sf.getOsgiService(serviceType, serviceName); } } |
Step5.Web調用示例。
public static String getUserName(String id) { try { IUserService service = OsgiServiceFacade.getOsgiService( "osgi/services", IUserService.class, IUserService.class .getName());
return service.getUserName(id); } catch (IllegalArgumentException e) { e.printStackTrace(); e.printStackTrace(); }
return null; } |
說明:
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,選擇standard OSGi framework。創建接口:
package com.dinstone.demo.user;
public interface IUserService { public String getUserName(String id); } |
Step2.創建插件工程com.dinstone.demo.user.db,創建類UserServiceImpl實現IUserService接口。
package com.dinstone.demo.user.db; import java.util.logging.Logger; import com.dinstone.demo.user.IUserService;
public class UserServiceImpl implements IUserService {
private static Logger log = Logger.getLogger(UserServiceImpl.class .getName());
@Override public String getUserName(String id) { log.info("get user name from db"); return "db" + id; } } |
Step3.向OSGi平臺發佈IUserService服務。創建Activator註冊服務對象。
package com.dinstone.demo.user.db;
import java.util.Properties;
import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration;
import com.dinstone.demo.user.IUserService;
public class Activator implements BundleActivator {
private ServiceRegistration serviceReg;
/* * (non-Javadoc) * * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) */ public void start(BundleContext context) throws Exception { Properties p = new Properties(); serviceReg = context.registerService(IUserService.class.getName(), new UserServiceImpl(), p); }
/* * (non-Javadoc) * * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) */ public void stop(BundleContext context) throws Exception { serviceReg.unregister(); }
} |
Step4. 創建插件工程com.dinstone.demo.user.file,創建類UserServiceImpl實現IUserService接口。
package com.dinstone.demo.user.file;
import java.util.logging.Logger;
import com.dinstone.demo.user.IUserService;
public class UserServiceImpl implements IUserService {
private static Logger log = Logger.getLogger(UserServiceImpl.class .getName());
@Override public String getUserName(String id) { log.info("get user name from file"); return "file" + id; } } |
Step5.向OSGi平臺發佈IUserService服務。創建Activator註冊服務對象。
package com.dinstone.demo.user.file;
import java.util.Properties;
import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration;
import com.dinstone.demo.user.IUserService;
public class Activator implements BundleActivator {
private ServiceRegistration serviceReg;
/* * (non-Javadoc) * * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) */ public void start(BundleContext context) throws Exception { Properties p = new Properties(); serviceReg = context.registerService(IUserService.class.getName(), new UserServiceImpl(), p); }
/* * (non-Javadoc) * * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) */ public void stop(BundleContext context) throws Exception { serviceReg.unregister(); } } |
4.2 創建Web應用
Step1.創建動態web工程webDemo,並新建類:UserServiceFacade。
package com.dinsotne.web.demo;
import com.dinsotne.web.osgi.OsgiServiceFacade; import com.dinstone.demo.user.IUserService;
public class UserServiceFacade {
public static String getUserName(String id) { try { IUserService service = OsgiServiceFacade.getOsgiService( "osgi/services", IUserService.class, IUserService.class .getName());
return service.getUserName(id); } catch (IllegalArgumentException e) { e.printStackTrace(); e.printStackTrace(); }
return null; } } |
Step2.創建index.jsp頁面。
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@page import="com.dinsotne.web.demo.UserServiceFacade"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Insert title here</title> </head> <body> User Name is <%=UserServiceFacade.getUserName("001") %>
</body> </html> |
Step3.修改web.xml文件,添加紅色部分。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns: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/javaee http://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>osgi service</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 發佈OSGi Bundle
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並安裝OSGi Bundle
Step1.在命令行下啓動Tomcat。E:/Cluster/apache-tomcat-6.0.18爲我的${Tomcat_Home}。
E:/Cluster/apache-tomcat-6.0.18>bin/startup.bat Using CATALINA_BASE: E:/Cluster/apache-tomcat-6.0.18 Using CATALINA_HOME: E:/Cluster/apache-tomcat-6.0.18 Using CATALINA_TMPDIR: E:/Cluster/apache-tomcat-6.0.18/temp Using JRE_HOME: C:/Program Files/Java/jdk1.6.0_10 2009-8-12 13:21:39 com.dinstone.tomcat.osgi.OsgiLifecycleListener lifecycleEvent 信息: The osgi content is initialized. Using osgi content:felix 2009-8-12 13:21:39 org.apache.coyote.http11.Http11Protocol init 信息: Initializing Coyote HTTP/1.1 on http-8080 2009-8-12 13:21:40 org.apache.coyote.http11.Http11Protocol init 信息: Initializing Coyote HTTP/1.1 on http-8443 2009-8-12 13:21:40 org.apache.catalina.startup.Catalina load 信息: Initialization processed in 1748 ms 2009-8-12 13:21:41 com.dinstone.tomcat.osgi.OsgiLifecycleListener lifecycleEvent 信息: Starting osgi service. 2009-8-12 13:21:41 com.dinstone.tomcat.osgi.felix.FelixContent start 信息: ********************************* 2009-8-12 13:21:41 com.dinstone.tomcat.osgi.felix.FelixContent start 信息: catalina home is E:/Cluster/apache-tomcat-6.0.18 2009-8-12 13:21:41 com.dinstone.tomcat.osgi.felix.FelixContent start 信息: osgi home is E:/Cluster/apache-tomcat-6.0.18/osgi/felix 2009-8-12 13:21:41 com.dinstone.tomcat.osgi.felix.FelixContent start 信息: ******user.dir is E:/Cluster/apache-tomcat-6.0.18
Welcome to Felix. =================
-> 2009-8-12 13:21:42 org.apache.catalina.core.StandardService start 信息: Starting service Catalina 2009-8-12 13:21:42 org.apache.catalina.core.StandardEngine start 信息: Starting Servlet Engine: Apache Tomcat/6.0.18 2009-8-12 13:21:42 org.apache.catalina.loader.WebappLoader start 信息: Dual registration of jndi stream handler: factory already defined 2009-8-12 13:21:44 org.apache.coyote.http11.Http11Protocol start 信息: Starting Coyote HTTP/1.1 on http-8080 2009-8-12 13:21:44 org.apache.coyote.http11.Http11Protocol start 信息: Starting Coyote HTTP/1.1 on http-8443 2009-8-12 13:21:44 org.apache.jk.common.ChannelSocket init 信息: JK: ajp13 listening on /0.0.0.0:8009 2009-8-12 13:21:44 org.apache.jk.server.JkMain start 信息: Jk running ID=0 time=0/47 config=null 2009-8-12 13:21:44 org.apache.catalina.startup.Catalina start 信息: Server startup in 3882 ms -> ps START LEVEL 1 ID State Level Name [ 0] [Active ] [ 0] System Bundle (1.6.0) [ 25] [Active ] [ 1] Apache Felix Shell Service (1.2.0) [ 26] [Active ] [ 1] Apache Felix Shell TUI (1.2.0) [ 27] [Active ] [ 1] Apache Felix Bundle Repository (1.4.0) -> |
Step2.安裝bundle。
-> install file:E:/Cluster/apache-tomcat-6.0.18/osgi/felix/bundle/com.dinstone.demo.user_1.0.0.jar Bundle ID: 39 -> install file:E:/Cluster/apache-tomcat-6.0.18/osgi/felix/bundle/com.dinstone.demo.user.db_1.0.0.jar Bundle ID: 40 -> install file:E:/Cluster/apache-tomcat-6.0.18/osgi/felix/bundle/com.dinstone.demo.user.file_1.0.0.jar Bundle ID: 41 -> ps START LEVEL 1 ID State Level Name [ 0] [Active ] [ 0] System Bundle (1.6.0) [ 25] [Active ] [ 1] Apache Felix Shell Service (1.2.0) [ 26] [Active ] [ 1] Apache Felix Shell TUI (1.2.0) [ 27] [Active ] [ 1] Apache Felix Bundle Repository (1.4.0) [ 39] [Installed ] [ 1] User Model Interface Plug-in (1.0.0) [ 40] [Installed ] [ 1] User DB Implement Plug-in (1.0.0) [ 41] [Installed ] [ 1] User File Implement Plug-in (1.0.0) -> |
Step3.訪問web應用,http://localhost:8080/webDemo/。由於沒有啓動OSGi服務,故出現500異常頁面,錯誤原因是沒有找到服務。
root cause com.dinsotne.web.osgi.IllegalServiceException: Cann't find out osgi service:com.dinstone.demo.user.IUserService
com.dinsotne.web.osgi.OsgiServiceInvocationHandler.invoke(OsgiServiceInvocationHandler.java:30)
$Proxy0.getUserName(Unknown Source)
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.啓動User DB Implement Plug-in服務,激活User模塊的DB實現。
->ps START LEVEL 1 ID State Level Name [ 0] [Active ] [ 0] System Bundle (1.6.0) [ 25] [Active ] [ 1] Apache Felix Shell Service (1.2.0) [ 26] [Active ] [ 1] Apache Felix Shell TUI (1.2.0) [ 27] [Active ] [ 1] Apache Felix Bundle Repository (1.4.0) [ 39] [Installed ] [ 1] User Model Interface Plug-in (1.0.0) [ 40] [Installed ] [ 1] User DB Implement Plug-in (1.0.0) [ 41] [Installed ] [ 1] User File Implement Plug-in (1.0.0) -> start 40 -> ps -s START LEVEL 1 ID State Level Symbolic name [ 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. 啓動User File Implement Plug-in服務,激活User模塊的File實現。
-> ps START LEVEL 1 ID State Level Name [ 0] [Active ] [ 0] System Bundle (1.6.0) [ 25] [Active ] [ 1] Apache Felix Shell Service (1.2.0) [ 26] [Active ] [ 1] Apache Felix Shell TUI (1.2.0) [ 27] [Active ] [ 1] Apache Felix Bundle Repository (1.4.0) [ 39] [Resolved ] [ 1] User Model Interface Plug-in (1.0.0) [ 40] [Active ] [ 1] User DB Implement Plug-in (1.0.0) [ 41] [Installed ] [ 1] User File Implement Plug-in (1.0.0) -> start 41 -> ps START LEVEL 1 ID State Level Name [ 0] [Active ] [ 0] System Bundle (1.6.0) [ 25] [Active ] [ 1] Apache Felix Shell Service (1.2.0) [ 26] [Active ] [ 1] Apache Felix Shell TUI (1.2.0) [ 27] [Active ] [ 1] Apache Felix Bundle Repository (1.4.0) [ 39] [Resolved ] [ 1] User Model Interface Plug-in (1.0.0) [ 40] [Active ] [ 1] User DB Implement Plug-in (1.0.0) [ 41] [Active ] [ 1] User File Implement Plug-in (1.0.0) -> |
訪問http://localhost:8080/webDemo/。頁面顯示:
Step6.現在停止User DB Implement Plug-in服務。
-> ps START LEVEL 1 ID State Level Name [ 0] [Active ] [ 0] System Bundle (1.6.0) [ 25] [Active ] [ 1] Apache Felix Shell Service (1.2.0) [ 26] [Active ] [ 1] Apache Felix Shell TUI (1.2.0) [ 27] [Active ] [ 1] Apache Felix Bundle Repository (1.4.0) [ 39] [Resolved ] [ 1] User Model Interface Plug-in (1.0.0) [ 40] [Active ] [ 1] User DB Implement Plug-in (1.0.0) [ 41] [Active ] [ 1] User File Implement Plug-in (1.0.0) -> stop 40 -> ps START LEVEL 1 ID State Level Name [ 0] [Active ] [ 0] System Bundle (1.6.0) [ 25] [Active ] [ 1] Apache Felix Shell Service (1.2.0) [ 26] [Active ] [ 1] Apache Felix Shell TUI (1.2.0) [ 27] [Active ] [ 1] Apache Felix Bundle Repository (1.4.0) [ 39] [Resolved ] [ 1] User Model Interface Plug-in (1.0.0) [ 40] [Resolved ] [ 1] User DB Implement Plug-in (1.0.0) [ 41] [Active ] [ 1] User File Implement Plug-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結論
通過以上的測試,我們發現以上的實現基本符合最初的設想:
l OSGi的集成對Tomcat幾乎是透明的。
l OSGi的所有優點。
l Web表現和業務邏輯的完全分離。
l 基於模塊化服務的編程模型。
同時,我們也發現了一些問題:
l Web層沒有支持模塊化、可熱插拔的編程模型。
l OSGi層的服務日誌跟web層的日誌分離增加了維護的難度。
l 該集成方式沒有經嚴格測試,雖然已經有產品應用了。
附錄:
1.測試Demo
…
2.源碼工程
…