摘要: 使用Selenium測試showModalDialog模態對話框一直是一件困難的事情,本文提出一種hack解決的方法。
閱讀全文
posted @
2009-07-27 21:17 ronghao 閱讀(3592) |
評論 (0) |
編輯 收藏
現在流行抱大腿,不過對眼光的要求也高。要不就如高也,即使四眼,一樣無用。對Java企業開發而言,Spring的腿則是一定要抱的。而所謂抱Spring的腿,無外乎三點:
一是通過Spring暴露出服務,將服務配置到Spring的IOC容器里;
二是在自己的運行環境里訪問到Spring的IOC容器,能夠輕松使用Spring容器里所配置的服務;
三是對于具有事務管理特性的項目來說,將事務管理與Spring的事務管理進行合并。
下面分別討論:
一、 通過Spring暴露服務
還記得在jBPM4的運行期環境里提到的JbpmConfiguration嗎?它是整個jBPM4的入口,并且是整個應用獨此一份的。通過它可以獲取processEngine,并藉此獲得工作流引擎所提供的各種服務:
ProcessEngine processEngine = new Configuration()
.buildProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
ExecutionService executionService = processEngine.getExecutionService();
TaskService taskService = processEngine.getTaskService();
HistoryService historyService = processEngine.getHistoryService();
ManagementService managementService = processEngine.getManagementService();
通過Spring暴露這些服務,配置如下:
<bean id="jbpmConfiguration" class="org.jbpm.pvm.internal.cfg.SpringConfiguration">
<constructor-arg value="be/inze/spring/demo/jbpm.cfg.xml" />
</bean>
<bean id="processEngine" factory-bean="jbpmConfiguration" factory-method="buildProcessEngine" />
<bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
<bean id="executionService" factory-bean="processEngine" factory-method="getExecutionService" />
細心的你會發現,配置時使用了JbpmConfiguration 的子類SpringConfiguration。SpringConfiguration相比JbpmConfiguration有哪些增強呢,下面再講。總之,現在,就可以使用Spring來獲取或注入這些Jbpm4所提供的服務了。
二、在environment里加入SpringContext
jBPM4的environment(運行期環境)提供Engine IOC(process-engine-context)和Transaction IOC(transaction-context)。要想在運行期方便地訪問到Spring里所配置的服務,最直接的方法就是在environment里加入Spring IOC(applicationContext)的引用。
SpringConfiguration即是對JbpmConfiguration增強了對Spring IOC的一個引用。

SpringConfiguration是如何做到的呢?簡單,實現Spring的ApplicationContextAware接口,自動持有applicationContext,然后openEnvironment時將其加入environment。
environment.setContext(new SpringContext(applicationContext));
SpringContext是對applicationContext的簡單封裝。
那么什么從Engine IOC移民到Spring IOC了呢?是的,最重要的就是Hibernate Session Factory。
在jbpm.cfg.xml的process-engine-context里干掉:
<hibernate-configuration>
<cfg resource="jbpm.hibernate.cfg.xml" />
</hibernate-configuration>
<hibernate-session-factory />
相關配置挪動至Spring配置文件。
三、 事務
哪里有數據庫操作,哪里就有事務。對于嵌入式工作流而言,最重要的集成就是事務的集成。這里先分析jBPM4的事務實現,然后再介紹集成入Spring的事務實現。
1、 Command模式
jBPM4的邏輯實現采用了Command模式。

采用Command模式后,jBPM4對CommandService構造攔截器(Interceptor)鏈,配置在jbpm.cfg.xml的process-engine-context里:
<command-service>
<retry-interceptor />
<environment-interceptor />
<standard-transaction-interceptor />
</command-service>
2、 原有的事務實現
jBPM4原有的事務通過StandardTransactionInterceptor實現,在CommandService執行Command之前打開事務(實際委派Hibernate的事務管理),完成后提交/回滾。

jBPM4的事務是基于Command的。
3、 集成入Spring的事務實現
Spring的事務是基于服務調用的。

使jBPM4使用Spring提供的事務:
<command-service>
<retry-interceptor />
<environment-interceptor />
<spring-transaction-interceptor current="true" />
</command-service>
攔截器換用
SpringTransactionInterceptor,SpringTransactionInterceptor從environment 提供的Spring IOC獲取PlatformTransactionManager,使用事務模板回調Command,事務傳播模式強制加入當前事務。
同時,對hibernate session的配置(jbpm.cfg.xml的transaction-context)強制從當前線程中獲取:
<hibernate-session current="true"/>
并干掉原有的事務實現:
<transaction />
參考文檔:
http://www.slideshare.net/guest8d4bce/spring-integration-with-jbpm4
posted @
2009-06-22 16:38 ronghao 閱讀(7373) |
評論 (7) |
編輯 收藏
萬物生長靠太陽,兒童的生長離不開土壤、空氣和水,當然,也離不開綠壩娘的調教。應用程序也是如此,離不開數據庫連接、事務、日志、消息等,這些,共同構成了應用程序的運行期環境。
理想中的環境是什么樣子的哩。好吧,一句話,召之即來,揮之即去,當需要某個服務時,ok,打個響指,該服務就準備好被調用了,調用完畢后也不用費心費力地擦屁股,不必老是提心吊膽有好事者追問:你擦了嗎,確定擦了?真的確定擦了?直接丟棄給環境降解處理,自然又環保,還有個好名聲叫專注領域邏輯。
一、 運行期環境就是一個餐館
1、 提供必要的服務
作為一個餐館,必須有廚師做飯我吃,必須有桌子和椅子。作為運行期環境同樣如此,我要發消息,你得提供我發消息的Service,我要獲取節點任務,你得扔給我TaskService。
2、 提供獲取這些服務的統一方式
好吧,我不會親自到廚房告訴廚師我想吃什么(因為我擔心這樣一來我會吃不下去),我也不會親自到收銀臺給錢。這些服務有一個統一的獲取方式:服務員。我想吃什么和結賬,告訴服務員即可。關鍵是這一方式要統一,要足夠簡單。Spring最懶,把服務給你全部注入了,當然你也可以握住BeanFactory的纖纖細手,一個一個的get。
3、 提供特定于我線程不安全的服務
我點了一盤魚香肉絲,隔壁也點了一盤魚香肉絲,結果服務員讓我們吃同一盤魚香肉絲。我立刻跳起來:靠,你們的服務不是線程安全的嗎?!Hibernate的Session正是屬于這么一種情況,需要環境進行隔離,我的唯一職責就是吃飯!我的領域邏輯是如何優美的進餐!為此還要不斷重構我吃飯的姿勢哩。
好不容易吃完飯,付完款,正準備離場。服務員風度翩翩地走到我的身旁,我以為還有打折券供應,結果是:服務員小姐輕啟朱唇:先生,麻煩您把吃剩的盤子清洗完畢。
崩潰!
像數據庫連接的打開,關閉、事務的打開、提交等都屬于運行期環境應該做的事情。
4、 其他的七七八八
雜事不少,例如統一的事件機制、權限攔截等等。
二、 jBPM4的運行期環境
好吧,先來看看如何建立jBPM4的運行期環境:
EnvironmentFactory environmentFactory = new DefaultEnvironmentFactory();

Environment environment = environmentFactory.openEnvironment();
try {
everything available in this block 
} finally {
environment.close();
}
兩個關鍵的類:EnvironmentFactory和Environment。
EnvironmentFactory是全局的,在整個應用程序中保持一個實例即可。
Environment則是每次方法調用則要new一個。
看看Environment的主要方法:
public abstract Object get(String name);
public abstract <T> T get(Class<T> type);
是的,environment為我們的代碼提供所需要的服務類實例。
那么,如何獲得environment?
繼續看:
public static Environment getCurrent();
static,我喜歡也。方便、快捷,不管是在地上、車上還是房頂上,隨處都可調用。
那么,為什么Environment每次調用要new呢?
好吧,當你需要獲取數據庫Session的時候,是不是每次都要new呢。Environment提供的服務里包括了非線程安全的數據庫操作服務。
三、 jBPM4運行期環境的實現
1、JbpmConfiguration
JbpmConfiguration是jBPM4里最重要的類,它是整個應用程序的入口。它實現了EnvironmentFactory接口。
JbpmConfiguration加載jBPM總的配置文件,還是大概掃一下這個配置文件:
<jbpm-configuration xmlns="http://jbpm.org/xsd/cfg">
<process-engine-context>
<repository-service />
<repository-cache />
<execution-service />
<history-service />
<management-service />
<identity-service />
<task-service />
<hibernate-configuration>
<cfg resource="jbpm.hibernate.cfg.xml" />
</hibernate-configuration>
<hibernate-session-factory />
</process-engine-context>
<transaction-context>
<repository-session />
<pvm-db-session />
<job-db-session />
<task-db-session />
<message-session />
<timer-session />
<history-session />
</transaction-context>
</jbpm-configuration>
配置文件被分為了兩部分,分別是:process-engine-context和transaction-context。
對應于兩個IOC容器(WireContext)的配置文件。
作為EnvironmentFactory,JbpmConfiguration持有成品process-engine-context對應的IOC容器(全局的)實例,持有半成品transaction-context的WireDefinition。當調用openEnvironment方法時,JbpmConfiguration會new Environment,然后將process-engine-context IOC填充入environment,同時初始化transaction-context IOC,并將其也填充入environment。這樣通過environment就可以獲得所有所需要的服務,包括全局的和非線程安全的服務實例。也就是environment透過IOC容器提供了查找各種服務的能力。
2、與線程綁定的environment
environment初始化之后,避免參數傳遞得一塌糊涂的方式就是將environment與線程綁定。看Environment的代碼:
static ThreadLocal<Environment> currentEnvironment = new ThreadLocal<Environment>();
static ThreadLocal<Stack<Environment>> currentEnvironmentStack = new ThreadLocal<Stack<Environment>>();
是的,在openEnvironment時,有這么一行代碼:
Environment.pushEnvironment(environment);
這樣environment就與線程綁定了,可以通過Environment.getCurrent()任意調用了。
哪里有壓迫,哪里就有放抗。
在environment.close()方法里:
Environment.popEnvironment();
OK,結束。
posted @
2009-06-17 18:15 ronghao 閱讀(2813) |
評論 (5) |
編輯 收藏
一、目前的情況
目前我們要進行持續集成的對象是一個有著100人左右的開發團隊,他們開發著一套很龐大的系統。整個開發團隊劃分為多個開發小組進行協同開發,每個開發小組負責2-3個模塊的開發,實際這里的模塊已經相當于一個中小型系統。各模塊所有的類都通過eclipse整體編譯在一起,直接放置在WEB-INF/classes下。本地是無法啟動整個系統的,需要耗費大量的資源。
二、碰到的問題
在了解具體情況之前,我們最初的想法是為整個產品做一個持續集成,但是很快就發現這一想法存在很多的問題:
1、整個產品每次構建的時間會很長,這個時間包括代碼的編譯、啟動Weblogic,完成自動化測試,同時對服務器的硬件要求非常高
2、因為構建時間長,所以如果本地構建通過后再提交會嚴重影響開發效率,況且本地的硬件條件很可能啟動不了
3、如果本地不構建提交,則由于開發人數眾多,構建會非常不穩定,會經常處于失敗狀態。而構建失敗會導致后續提交的阻塞。
4、作為一個100人的開發團隊,代碼提交會引發頻繁的服務器構建,服務器無法負擔。
同時作為客戶,他們有這樣一種想法:敏捷開發是好的,但是不適合于大的項目和大的團隊。
最重要的問題集中在兩個方面:
1、啟動整個產品過于重量級(不包括自動化測試的情況下已經如此)
2、如何不影響開發人員的頻繁提交
三、我們的想法
我們現在的想法是做多階段的持續集成(multi-stage CI)
可以參考這里
http://www.ddj.com/development-tools/212201506
具體而言:
1、各個開發小組內做小組內的持續集成
2、開發小組間集成做整個產品的持續集成
大概:
1、每個開發小組一個分支,整個產品一條主線
2、在小組分支上搭建持續集成環境,小組內的開發向該分支上提交,各個小組可以并發開發,互不影響
3、小組完成一個完整的功能后,從主線更新合并代碼,本地構建通過,提交,觸發整個產品的持續集成
為使小組內持續集成構建加快,小組內盡量劃分清楚對其他模塊的依賴,不必要的模塊(這里的模塊包括基礎模塊,例如工作流模塊)不必加載。
同時推薦輕量級的web服務器例如Tomcat來完成小組內的測試環境。需要啟動weblogic的情況或功能依賴過多的情況下,建議在產品持續集成時進行測試。
同時保留原有的啟動單獨測試服務器進行手工測試的習慣。
posted @
2009-05-26 23:13 ronghao 閱讀(1526) |
評論 (0) |
編輯 收藏
和Jbpm3一樣,Jbpm4實現了自己的IOC容器。以現在的眼光看來,應用程序里一個IOC容器幾乎是居家必備的,否則,又要平白多出一坨一坨的工廠類和單態類來。
一、 Jbpm4 IOC容器介紹
IOC容器的目的是管理組件和實現組件之間的解耦。和Spring里的BeanFactory對應,Jbpm4里的接口是Context,具體實現則是WireContext。Context實際在Jbpm4里有更多的含義,它與Environment一起,共同構成了代碼運行的運行期環境。在這個環境里可以獲取系統的組件,更為重要的是提供了數據庫連接(session)和事務(這個稍后會講)。
先來看看Context接口的核心方法:
Object get(String key);
<T> T get(Class<T> type);
很明顯,提供兩種從容器里獲取組件的方法,一種是通過name,一種是通過type。
對于IOC容器來說,一般情況下都會提供一種加載的方式,比如從xml文件進行加載、從資源文件進行加載。Jbpm4透過WireParser具備從xml加載的能力。
此外,WireContext通過一個Map緩存初始化后的組件。
二、 Jbpm4 IOC容器實現
容器的實現有五個關鍵類和接口,分別是:WireParser、Binding、Descriptor、WireDefinition和WireContext。

WireParser讀取xml文件,同時WireParser會加載一系列的Binding(默認從jbpm.wire.bindins.xml文件讀取加載)。
Binding負責根據xml里元素的tag將xml元素轉換為對應的Descriptor。
Descriptor負責初始化對象。它們被添加到WireDefinition。
WireDefinition被WireParser返回給WireContext。WireContext創建對象時會訪問WireDefinition里的Descriptor,同時將初始化對象的任務委托給Descriptor自身。
需要注意的是:Jbpm4在初始化對象時有著四種策略,分別是:延遲創建和初始化、延遲創建和立刻初始化、立刻創建和延遲初始化、立刻創建和立刻初始化。
立刻創建:在WireContext創建完畢后對象就已經創建。
延遲創建:調用WireContext的get方法獲取該對象時才創建該對象。
初始化:一般完成對象屬性的注入等操作。
三、 Jbpm4 IOC容器在Jbpm4里的應用
IOC容器在Jbpm4里最重要的作用就是加載Jbpm的總的配置文件(默認是jbpm.cfg.xml),這也是整個Jbpm應用的起點。大概掃一下這個配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<jbpm-configuration xmlns="http://jbpm.org/xsd/cfg">
<process-engine-context>
<repository-service />
<repository-cache />
<execution-service />
<history-service />
<management-service />
<identity-service />
<task-service />
<hibernate-configuration>
<cfg resource="jbpm.hibernate.cfg.xml" />
</hibernate-configuration>
<hibernate-session-factory />
</process-engine-context>
<transaction-context>
<repository-session />
<pvm-db-session />
<job-db-session />
<task-db-session />
<message-session />
<timer-session />
<history-session />
</transaction-context>
</jbpm-configuration>
可以看到配置文件被分為了兩部分,分別是:process-engine-context和transaction-context。在實際應用中,它們分別對應著兩個不同的WireContext:ProcessEngineContext和TransactionConext。ProcessEngineContext覆蓋了jbpm4里最重要的服務類,這些類是全局唯一的,當然,ProcessEngineContext也是獨此一份。本是同根生,命運各不同。TransactionConext則是在每次openEnvironment時重新創建,因為其包含了數據庫連接和事務。
貫穿于整個Jbpm4中,這兩個Context被壓到Environment里(Environment和線程綁定),在任何需要的地方都能提供一條龍的服務。于是,在很多領域類里,利用這些服務實現充血模型就是很順理成章的一件事了。
總結: ProcessEngineContext給引擎領域模型提供全局的組件查找;TransactionConext提供數據庫相關服務。
posted @
2009-05-07 18:43 ronghao 閱讀(3696) |
評論 (2) |
編輯 收藏