基於OSGi的企業級開發框架實踐——全局參數工具

在接下去的幾篇文章中,我們將通過一些最佳實踐來繼續討論我們的開發框架。做爲一個應用系統,全局參數配置是一個不可或缺的重要工具。它可以爲系統初始化提供必須的參數,也可以爲系統提供一些用戶自定義的個性化參數,總之它關係到整個應用系統的正確運行。在上一篇文章中,我們已經認識了它,現在就讓我們更加深入的瞭解它的廬山真面目。當你啓動了開發框架的運行時環境後,會在Eclipse Console中看到如下圖所示的日誌信息:


(圖一)

在開發框架運行時容器啓動的時候會自動的加載指定目錄下的system.properties文件,該文件就是默認的全局參數配置文件,比如上例中的D:\home\admin\share\data\helloworld\system.properties。在assembly目錄下提供了一個system.properties文件的默認模板,請在第一次啓動開發框架時複製該文件到指定的目錄,比如上例中的D:\home\admin\share\data\helloworld目錄,否則就會看到如上一篇文章中所介紹的異常信息。下圖展示了system.properties文件提供的默認配置內容:


(圖二)system.properties內容

該文件中定義了一些默認的全局參數,比如:應用程序的工作目錄路徑,服務器的ID標識,數據庫驅動配置等(開發框架支持MySQL的Master/Slave模式的配置,從上圖中就可以看到,我們配置了2個數據庫的URL參數,後續文章將對數據庫相關操作進行詳細的討論)。當然你也可以添加自定義的參數,只要符合Java屬性文件的格式即可。接下去讓我們來看看全局配置參數加載器是如何工作的。請在Eclipse IDE中打開core-platform Bundle的bundle-context-osgi.xml文件,如下圖所示:


(圖三)

開發框架提供了一個OSGi的服務定義,org.storevm.eosgi.properties.loader.SystemPropertiesLoader,該服務用於在Bundle啓動時加載system.properties屬性文件。另外該服務還有一個“location”屬性,用於定義system.properties文件的路徑(如果路徑中不提供文件名,則使用默認的system.properties做爲文件名)。現在我們的全局參數已經全部被加載了,接下去我們將在程序中讀取這些全局參數。

請打開biz-share Bundle中的bundle-context-osgi.xml配置文件,如下圖所示:


(圖四)

在該文件中開發框架提供了全局配置參數加載器服務的引入配置,如下代碼:

<osgi:reference id="propertiesLoader" interface="org.storevm.eosgi.properties.loader.PropertiesLoader"/>

該配置將core-platform Bundle中發佈的OSGi服務引入到biz-share Bundle中,這樣在biz-share Bundle中就可以使用這個OSGi服務了,請看bundle-context.xml配置文件中的代碼,如下:

<!-- OSGi service annotation processor -->
<bean class="org.storevm.eosgi.core.annotation.OsgiServiceBeanPostProcessor" />
<bean class="org.storevm.eosgi.core.annotation.ServiceReferenceInjectionBeanPostProcessor" />
	
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" depends-on="propertiesLoader"/>

前2個配置是關於OSGi的annotation配置,下一章我們將對此進行詳細討論,這裏我們暫時忽略它。第三個bean配置是我們經常用到的Spring的佔位符替換的Bean。它依賴於我們的全局配置參數加載器服務的Bean。只有當PropertiesLoader服務實例化之後PropertyPlaceholderConfigurer纔會進行實例化,這樣,我們就可以在Spring XML中使用如下的配置了:

<bean id="masterDataSource" class="com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean">
	<property name="uniqueResourceName" value="${unique.resource.name.one}" />
	<property name="url" value="${jdbc.url.one}" />
	<property name="driverClassName" value="${jdbc.driverClassName}" />
	<property name="user" value="${jdbc.username}" />
	<property name="password" value="${jdbc.password}" />
	<property name="poolSize" value="20" />
	<property name="borrowConnectionTimeout" value="60" />
	<property name="testQuery" value="select 1" />
</bean>

其中用${}表示的佔位符就是配置在system.properties文件中的屬性值。

這裏可能會有人提出疑問,PropertyPlaceholderConfigurer不是也提供了一個locations屬性值用於指定屬性配置文件的路徑嗎?是的,但是這裏存在一個問題,PropertyPlaceholderConfigurer只能讀取本Bundle中的*.properties文件,如果其他Bundle要讀取相同的參數值,則需要在其他Bundle中重新再定義一個相同的*.properties文件,這是由OSGi的Classloader隔離機制所導致的,而且屬性文件被打包到Bundle中,我們就失去了在運行時修改配置參數的靈活性,所以開發框架提供了一個單獨的OSGi服務用於加載全局的配置參數,這樣所有的Bundle只要引入該OSGi服務就能讀取統一的配置參數了。以下代碼展示瞭如何在代碼中使用PropertiesLoader服務:

/**
 * 
 * @author Administrator
 * @version $Id: SystemPropertiesLoaderTest.java, v 0.1 2013-2-26 下午6:33:40 Administrator Exp $
 */
@Component
public class SystemPropertiesLoaderTest {
    private static final Logger LOGGER = Logger.getLogger(SystemPropertiesLoaderTest.class);

    /* 全局參數加載器 */
    private PropertiesLoader    propertiesLoader;

    /**
     * 讀取全局配置參數
     */
    @PostConstruct
    public void readProperties() {
        LogUtils.info(LOGGER, "讀取全局配置參數, share.data.path={0}",
            propertiesLoader.getProperty("share.data.path"));
    }

    /**
     * Setter method for property <tt>propertiesLoader</tt>.
     * 
     * @param propertiesLoader value to be assigned to property propertiesLoader
     */
    public void setPropertiesLoader(PropertiesLoader propertiesLoader) {
        this.propertiesLoader = propertiesLoader;
    }

}

從代碼中我們可以看到,在readProperties方法中我們讀取了“share.data.path”配置參數,從圖二中我們可以看到share.data.path的參數值。由於我們使用了annotation,所以無需再在spring的配置文件中註冊bean。在我們的開發框架中,所有bean的配置均使用annotation完成。好了,我們可以啓動Eclipse中的OSGi運行時容器了,點擊工具欄上的運行按鈕,並選擇“helloworld”,如下圖所示:

(圖五)

Console中會顯示大量的啓動日誌信息,如果運行正常,你將會看到如下圖所示的信息,表示我們的測試成功了:

(圖六)

從上圖可知,程序讀取的“share.data.path”配置參數值和我們在system.properties文件中配置的值完全一樣。目前我們使用的是開發框架提供的運行時環境來進行代碼測試,後續文章我們將介紹開發框架提供的另外一種集成測試方法。

接下來我們將在OSGi容器的運行過程中,修改system.properties中的參數值,然後看看,在biz-share Bundel中是否讀取到了修改之後的新值。

請在Console中輸入“ss”命令,然後會列出所有的Bundle,如下圖所示:

(圖七)

我們查找core-platform和biz-share Bundle的id值,如下圖所示(可能你的id與圖中的不一樣,以實際情況爲準):

(圖八)

從上圖可以看到,biz-share Bundle的id是116,而core-platform Bundle的id是118。我們修改system.properties文件中的“share.data.path”配置項,如下圖:

(圖九)

我們在Console中的“osgi>”提示符下繼續輸入如下命令:

stop 118

該命令將停止core-platform Bunlde的運行,在Console中會顯示如下提示信息(由於篇幅關係,這裏的提示信息進行的刪減):

osgi> stop 118
INFO [org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext] - Unpublishing application context OSGi service for bundle Helloworld...
INFO [org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext] - Closing OsgiBundleXmlApplicationContext(bundle=helloworld-core-plat...
INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Destroying singletons in org.springframework.beans.factory.support...
INFO [org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean] - Unregistered service [ServiceRegistrationWrapper for {org.storevm...
INFO [org.springframework.osgi.extender.internal.activator.ContextLoaderListener] - Application context succesfully closed (OsgiBundleXmlApplica...

接着我們輸入:

start 118

該命令將啓動core-platform Bundle(stop和start是常用的重啓Bundle的命令),在Console中會顯示如下提示信息(由於篇幅關係,這裏的提示信息進行的刪減):

INFO [org.springframework.osgi.extender.support.DefaultOsgiApplicationContextCreator] - Discovered configurations {osgibundle:/META-INF/spring/*.xml}...
INFO [org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext] - Refreshing OsgiBundleXmlApplicationContext(bundle=helloworld-core-p...
INFO [org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext] - Application Context service already unpublished

osgi> INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from URL [bundleentry://118.fwk18644877/META-...
INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from URL [bundleentry://118.fwk18644877/META-INF...
INFO [org.springframework.osgi.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor] - No outstanding OSGi service ...
INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Pre-instantiating singletons in org.springframework.beans.factory.support...
INFO [org.storevm.eosgi.properties.loader.PropertiesLoader] - 讀取到全局配置文件路徑, location=D:\home\admin\share\data\helloworld\system.properties
INFO [org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean] - Publishing service under classes [{org.storevm.eosgi.properties.loa...
INFO [org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext] - Publishing application context as OSGi service with properties {org...
INFO [org.springframework.osgi.extender.internal.activator.ContextLoaderListener] - Application context successfully refreshed (OsgiBundleXmlApplicatio...

從中我們可以看到,system.properties被重新加載了。

接着我們重啓biz-share Bundle,和之前重啓core-platform Bundle一樣的方法,輸入如下兩個命令:

stop 116
start 116

Console中會顯示如下信息:

(圖十)

我們在不停止OSGi容器的情況下,修改了全局配置參數,並讓其他Bundle可以讀取到修改之後的配置參數,這展現了OSGi在動態性方面的特性。在實際運行中,我們只需要重啓core-platform Bundle就可以了,這裏是爲了演示修改前和修改後的參數值差異,所以重啓了biz-share Bundle。

下一章中,我們將介紹開發框架提供的一些OSGi Annotations,它們可以幫助我們提高註冊OSGi服務和使用OSGi的效率。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章