2011年12月13日 星期二

BPM11g PS4FP新功能介紹 -- AlterFlow

最近太忙,好久沒有寫文章了。很快更新一下最新在BPM11g PS4FP的其中一項重要的新功能:AlterFlow (也就是在BPM 10g出現的Grab功能)。

AlterFlow的目的是提供特定人員透過手動介入流程的執行,以更改目前流程執行到的步驟、或是更動流程中變數資料。在BPM應用之中,此功能往往是使用者或管理人員在例外處理所需要的重要功能,現在終於在BPM11g PS4FP中出現了。


首先,我設計了一個很簡單流程如下:
















在流程中我定義了兩個人工關卡 - Step1與Step2,分別交給Role1與Role2兩種角色執行,另外一個在流程中的重要角色叫做Process Owner,此角色不需要由開發人員創建,基本上在每一個流程中系統會預設此角色,此角色在AlterFlow中十分重要,任何屬於此流程Process Owner角色的人員都可以使用AlterFlow功能介入人工處理。另外,在流程中我也定義了一個字串變數叫做msg,簡單用來記錄每個關卡之後的狀態。

部署並執行此流程後,先登入Role1的使用者完成Step1,接下來,請用擁有Process Owner角色的使用者登入BPM Workspace,選擇"處理追蹤"工作頁,按下"進階",並選擇搜尋條件,在此你可以選定指派給所有使用者(所有角色)的特定流程 - AlterFlow v1.0:












接下來,你可以找到目前正在執行中的所有AlterFlow流程instance,選定你要更動的instance之後,你可以看到到如下圖所示的目前流程執行狀態:















你可以清楚在"稽核歷程檔"以及"開啟的活動"中看到流程執行的歷史以及目前停留的活動位置。選擇"動作" --> "更改流程並暫停",你會見到如下圖所示的畫面,你可以在"新活動"中選擇你所希望流程跳到哪一個活動位置,如圖所示,我們可以把流程手動回到上一個Step1的步驟:


另外,如果你同時需要更動變數資料,以清除或還原流程狀態的話,你可以選擇中間變數並按下那一支筆的圖案:


接下來就可以改資料了,只不過是raw XML的格式,對一般使用者是比較苦了...

在台灣,有些客戶有特定的流程"抽件"需求,也就是,關卡已經送出了,但是在下一關人員尚未執行之前,可以將案件反悔抽回至自己手上,遇到這樣的需求,看來AlterFlow配合上API的前端客制是最容易實現的方式了。


2011年9月21日 星期三

11gR1PS4 BPM FP 正式發表

等待許久的11gR1PS4 BPM FP (Features Pack)終於發表了,雖然這個版本從名字看來不是那麼重要 -- 只是PS4 (11gR1的第四個Patch Set)中的BPM功能包,但從功能面來看,這個版本補足了許多之前版本所欠缺的重要功能,個人覺得重要的幾個新功能包括:
  • 新版本的BPM Composer:提供更完整的Web介面給Business User或Enterprise Architect設計流程專案,提供多版本管理與多人合作的功能,在我看來,新版本的BPM Composer可以讓未來流程設計可以真正由Business User透過網頁工具起頭,IT開發人員接手使用JDeveloper進行細項設計。
  • 流程實例的更動與移轉:可以讓有權限的人員更動正在執行的流程內容,這點可以滿足國內許多客戶希望管理人員可以隨時更動流程執行狀態的需求。另外,這版本也支援了流程實例的移轉,簡單來說,假設伺服器上已經有某流程被部署,我們再重新部署新版本流程之時,可以選擇保留舊版本流程實例繼續執行,接下來該流程的owner則可以在workspace之中找出需要進行移轉的實例進行直接移轉,也可以針對特定的流程實例更動流程實例資料內容,以確保移轉正確。
  • Correlations:基本上Correlation算是多流程實例交互引用時非常重要的功能,簡單來說,一個非同步的外部系統呼叫,在外部系統執行完成之後需要callback流程實例,這個時候就有個基本的問題,系統中可能有好多個流程實例都在等待callback,到底此時該找到那個流程實例呢? Correlation就是此問題的解決方案,之前版本的BPM不是不能解決這問題,只是要解決需透過BPEL的功能來做,拐了個彎相當不方便。最新版本此功能被新增至BPMN流程設計之中了。
  • 預先定義的流程變數:以往在流程之中要取得一些流程資訊如:instanceId、compositeDN、title、creationDate...等資料是有點麻煩的,通常需要透過XPATH呼叫Oracle的一些函式才能夠拿到,在開發上面比較繁瑣,這版本把這些資料放入了一些預設變數之中方便開發人員取用。
  • 新的Data Association Editor:簡單來說,有個新開發介面方便資料的對應的設計,看圖感受一下吧:

  • 規則引擎強化:新的規則引擎增加了測試功能與audit trail的紀錄,以後再BPM Console中查詢流程實例的執行歷程紀錄時,我們可以看到規則引擎的實行歷程,也算是方便未來的debug與trace。
  • Draft Mode and Log Messages:同樣是為了開發與測試的便利性,現在開發人員可以將流程中尚未開發完成的activity指定為draft狀態,指定為draft狀態的activity可以不需要指定詳細的實作方式,一樣可以被部署並進行測試,這和以前我們需要完成全部的流程開發之後才能進行部署測試是更佳的彈性了。另外,這版本也可以很簡單得在每一個activity之中加上你想要紀錄的訊息,當然,這就像是你用Log4J來記錄Java程式的執行狀態一樣,方便未來的除錯過程。
其餘還有一些功能如:Parametric role、Sticky user、Oracle UCM整合、Activity Guide、Notification Activity、Round-trip simulation與流程文件的產生...等,基本上算是很多新功能的版本了!

PS4FP是透過Patch的方式散佈,所以有興趣的朋友,你必須先有個安裝完成一個現有PS4環境,接下來透過Oracle Support找到Patch 12413651才能下載安裝檔。

後續有時間的時候,我會陸續做幾個簡單範例介紹這些新功能。So.... Stay tuned!



2011年7月5日 星期二

[Oracle BPM/SOA 實務] 應用外部LDAP Server做為使用者資料來源

BPM的應用往往會應用企業內部現有使用者資訊,最通常會遇到的就是和企業內部的MS Active Directory、Sun iPlanet Directory、OpenLDAP、Oracle Internet Directory或Novell Directory Server...等LDAP Server整合。在單機測試環境中,你可以應用WebLogic Server本身內建的LDAP Server進行開發,但是當部署到線上系統的時候,架構規劃人員就需要思考如何介接上述的LDAP Server了。在之前的文章中,我已經有介紹過如何建構開發環境,並使用WebLogic內建LDAP Server,本文我們就來看看怎麼和外部的LDAP Server整合。

為了測試方便起見,我們使用OpenLDAP做為範例,關於OpenLDAP的簡單使用方式,你可以參考此連結,若你使用Windows作業系統進行開發測試,請到此處下載Windows版本。另外,你會需要一個LDAP Client方便產生並修改使用者資料,我們這裡使用的是JXplorer,你可以使用任何熟悉的工具。

這裡我所使用的範例的base DN是dc=oracle,dc=com,如果你是重新建立一個新的OpenLDAP環境,可以修改slapd.conf檔案來去指定相關的suffixrootdnrootpw參數。如果你是使用現存環境的話,記得在接下來的範例之中,依據你環境中的base DN與密碼進行修正。


新建使用者與群組

下面我提供了一個簡單的LDIF檔案,你可以做些簡單的修改,並透過JXplorer將他餵入OpenLDAP之中:


dn: ou=people,dc=oracle,dc=com
ou: people
objectClass: top
objectClass: organizationalUnit


dn:cn=user1,ou=people,dc=oracle,dc=com
objectClass:inetOrgPerson
cn:user1
sn:user1
uid:user1
userPassword:welcome1



dn:cn=user2,ou=people,dc=oracle,dc=com
objectClass:inetOrgPerson
cn:user2
sn:user2
uid:user2
userPassword:welcome1


dn: ou=groups,dc=oracle,dc=com
ou: groups
objectClass: top
objectClass: organizationalUnit


dn: cn=testGroup,ou=groups,dc=oracle,dc=com
objectClass: top
objectClass: groupOfNames
cn: testGroup
member: cn=user2,ou=people,dc=oracle,dc=com



請注意,我在這裡面建立了兩個OU,一個DN是ou=people,dc=oracle,dc=com,代表了使用者,另一個是代表使用者群組的ou=groups,dc=oracle,dc=com。同時,我順便建立了user1user2兩個使用者以及一個testGroup使用者群組。

從JXplorer你將看到這樣的畫面資訊:














特別注意,在BPM應用之中,你會用到很多額外的資訊,像是使用者的姓名、顯示名稱、職稱、Email、電話以及主管是誰...等等。因此,這些使用者所設定的objectClass最好是指定為inetOrgPerson。在簡單的建立了使用者之後,建議你可以透過LDAP Client連進去修改使用者完整的資訊。

這樣你的LDAP Server環境已經準備完成了,準備設定BPM Server的連接了!


設定BPM Server

首先,請登入WebLogic Console,並選擇【安全範圍】-->【myrealm】-->【提供者】。首先,你會看到兩個預設的認證提供者【DefaultAuthenticator】與【DefaultIdentityAsserter】,此時請按下新增按鍵,如下圖所示,新建一個OpenLDAP的認證提供者。

















按下確定鍵之後,你會看到新的認證提供者已經產生了,請點選這個新的提供者,並選擇【提供者特有】進行設定:


















在連線部分,請指定你OpenLDAP Server的主機hostname與port,並且指定所使用的密碼。
接下來是一堆我有修改的設定,請也依據修改:


使用者
使用者基本 DN: ou=people, dc=oracle, dc=com
名稱篩選中的使用者: (&(cn=%u)(objectclass=inetOrgPerson))
使用者物件類別: inetOrgPerson


群組
群組基本 DN: ou=groups, dc=oracle, dc=com
名稱篩選中的群組: (&(cn=%g)(objectclass=groupOfNames))


靜態群組
靜態群組物件類別: groupOfNames
成員 DN 篩選中的靜態群組 DN:(&(member=%M)(objectclass=groupOfNames))

完成之後,記得將你新建認證提供者的【控制旗標】設定為【SUFFICIENT】,如下圖所示:



















同時,請將DefaultAuthenticator的控制旗標設定為【OPTIONAL】,並且重新排序各個提供者,將你所新建的認證提供者放到最上端。


















重新啟動Server,這次你在myrealm之中,可以看到來自不同認證提供者的使用者:



恭喜你! 所以設定已經完成,你可以試試看用user1登入BPM Composer了! 你可以試試看在新的使用者上面測試之前我們介紹過的Multi-Tier審核流程囉!

2011年5月17日 星期二

[Oracle BPM/SOA 基礎] BPM/SOA Suite 11gR1PS4 使用 JRockit VM

最近Oracle悄悄的出了新版的Fusion Middleware,這次的版本是11gR1PS4,手癢的我趕緊抓了下來安裝,基本上想要嘗鮮新功能的開發人員可能要失望了,這個版本算是的bug-fix release,也因此在BPM/SOA部分沒有添加啥特別的新功能,到是修正了一些PS3所出現的bug,同時也做了一些效能的調教,有興趣的人可以去MetaLink看看Doc ID:1316076.1和1316062.1。


到是我在測試使用JRockit做為JVM的時候遇到了一些問題:

  • 安裝最新JRockit:R28.1.3
  • 修改setDomainEnv.cmd,改使用JRockit:
    • set BEA_JAVA_HOME=D:\jrockit\jrockit-jdk1.6.0_24-R28.1.3-4.0.1
    • set JAVA_VENDOR=Oracle

接著再啟動的時候會在console一直跑出下列資訊 (但是伺服器是正常作業的喔):

[WARN ][osal   ] Could not enumerate processes (1) error=-1073738819
[WARN ][osal   ] Could not add counter (null)\ for query
[WARN ][osal   ] Failed to init virtual size counter.



JRockit R28 Known Issues的文件中有明白指出,這樣的問題有可能是Windows PerfOS counter沒有被enable (抱歉,我不知道這是啥意思),按照文件指示到Microsoft網站上下載了工具後發現,我的設定一切正常,但是問題卻一再發生。


網路上爬了一下文發現,這樣的問題有可能是我的performance counter已經毀損,並且可以透過下列指令重建:


C:\Windows\system32>lodctr /r


執行過後就解掉我的問題了,提供給遇到相同問題的同好參考!





2011年4月20日 星期三

[Oracle WebLogic Server] Off-loading網站程式session資料至其他JVM

不知道大家有沒有遇過這樣的情形:

有時候因為環境因素,你所使用的application server是跑在32-bit的JVM之上,也因此,受限於JVM process能夠allocate到記憶體的大小,你的application server能夠使用到的heap大小在東扣西扣之後所剩不多,而某些的應用系統又特別需要記憶體空間,因為他們將大量的資料放在user session之中...

也許有人會說,那樣就使用cluster架構啊,這樣可以用多台application server分散user執行的分布,這樣的話,JVM會有比較多heap space存放session資料。但是只要你的cluster架構有使用到session-replication的話,基本上,你的每一個application server除了本身的session資料之外,還需要幫至少另外一台application server備份他的session資料,所以算來算去,你的heap space還是有可能會不夠。

64-bit JVM? 是的,這算是解法之一,但是要特別注意的是當heap space開到太大的時候,可能伴隨而來的long GC pause time問題。


來看看一個WebLogic的功能(GlassFish也最近也開始提供了喔...),我們讓WebLogic Server把session的資料存放在另外一到多個JVM上,而這些JVM可以被任意部署在多個硬體主機之上。這樣的架構帶來的好處是,你的session【理論上】可以存放到非常、非常多的資料。更棒的是,你可以讓不同的網頁應用程式共用session (想像一下使用者的shopping cart資料可以在A application與B application之中共用),甚至,你可以讓不同廠牌的application server互相做到session replication的機制。
"註:其實此機制不只支援WebLogic與GlassFish Server,也支援IBM WebSphere、JBoss以及Apache Tomcat等... application server,使用WebLogic是因為有現成的SPI可以直接放在application server之上,方便說明。"

跨JVM共用儲存資料? 這樣的技術其實並不是為了session資料複製所存在的,最早的出現是為了實現分散式快取 (Distributed Cache) 或分散式雜湊表 (Distributed Hash Table)而出現的,Oracle數年前收購了一間公司Tangosol,其產品:【Coherence】就是在做這件事情的,上面所說到將session資料丟到其他的JVM中,應用的就是Coherence技術的加工。我在這裡並不詳細說明Coherence,有興趣的人可以參考小白的介紹


環境準備

首先,如果沒有WebLogic最新版本,可以在這邊取得WebLogic 10.3.4的版本,此版本應該已經內含Coherence 3.6版。安裝完成後,請透過Config Wizard自己產生一個新的domain,其中至少需要包含兩個managedServer:server1與server2,並且建立一個cluster:cluster1,包含server1與server2。除此之外,這次我們建立的環境將會讓管理人員透過WebLogic Admin Console執行所有server的開啟與關閉,所以也請在domain中使用node manager。

另外,你會需要架設一個Apache HTTPD加上WebLogic Proxy module,由Apache扮演前端proxy與request dispatcher的角色,我的做法是Apache聽80 port然後dispatch給server1的7002 port與server2的7003 port。

最後,你會需要一個小application,其中會紀錄session資料的。我是用一個簡單的JSP,裡面會將此session連進此JSP網頁的次數顯示出來。


開始設定

請連到WebLogic Admin Console,展開<domain_name> --> 環境,將看到新的選項:Coherence伺服器:



如上圖,你可以透過【新建】功能,產生一到多個Coherence Server,每一個代表一個獨立的JVM提供給WebLogic Server存放session資料,至於需要多少個Coherence Server就端看你需要存放多大的session量來決定,我這邊先放兩個Coherence Server:coh-server1與coh-server2。

建立Coherence Server時,基本設定只需要很簡單的指定名稱與機器:


重點在完成新增之後,你需要點進去此Coherence Server指定他的啟動引數與類別路徑:

類別路徑請設定:
<MW_HOME>/coherence_3.6/lib/coherence.jar;<MW_HOME>/coherence_3.6/lib/coherence-web-spi.war;<MW_HOME>/modules/features/weblogic.server.modules.coherence.server_10.3.4.0.jar

引數:
-Xms256m -Xmx256m -Dtangosol.coherence.cacheconfig=WEB-INF/classes/session-cache-config.xml -Dtangosol.coherence.session.localstorage=true

完成後,請啟動這兩個Coherence Server。
"注意:<MW_HOME>是你WebLogic安裝的Middleware目錄,你可以任意修改引數中的JVM heap設定,另外,已經熟悉Coherence的人,請不要懷疑,這裡localstorage的設定是-Dtangosol.coherence.session.localstorage=true,不要把session改成distributed了"

接下來,使用WebLogic Admin Console在server1與server2上面部署下列的程式庫
  • <MW_HOME>/wlserver_10.3/common/deployable-libraries/active-cache-1.0.jar
  • <MW_HOME>/coherence_3.6/lib/coherence-web-spi.war


完成後,請將server1與server2啟動。

到此為止,你在伺服器上面設定已經完成了! 接著要做的是你每個網頁應用程式在WAR檔打包時該修改的:
  • <MW_HOME>/coherence_3.6/lib/coherence.jar檔打包進WEB-INF/lib之下
  • 編輯WEB-INF/weblogic.xml檔,新增:
    <wls:library-ref>  <wls:library-name>coherence-web-spi</wls:library-name></wls:library-ref>
  • 編輯META-INF/manifest.mf,放入:
    Extension-List: active-cacheactive-cache-Extension-Name: active-cacheactive-cache-Specification-Version: 1.0active-cache-Implementation-Version: 1.0
將你的WAR檔部署到cluster1上吧!

恭喜你! 你已經完成了!

透過你的Apache連入你的網頁程式,此時你應該可以任意的關閉server1與server2其中一台WebLogic Server。好玩的是,只要你Coherence Server還在啟動的狀態,你同時將server1與server2都關掉,在重新啟動其中任何一台之後,你會發現session還是存在,這就代表了你的session是被獨立存在Coherence Server的JVM上。

Enjoy!




2011年4月13日 星期三

[Oracle BPM/SOA 實務] 使用API查詢Composite instance

很多專案進行的過程中會有需要客制化一些前端應用,提供使用者查詢SOA/BPM平台中,特定的composite執行的狀態,來了解流程執行是否已經完成,或是執行內容是不是錯誤... 等。這篇文章我們就簡單介紹怎麼使用API來應用客制化:

首先,你需要引用下列JAR Library:
  • <mw_home>/wlserver_10.3/server/lib/weblogic.jar
  • <mw_home>/oracle_common/webservices/wsclient_extended.jar
  • <mw_home>/oracle_common/soa/modules/oracle.soa.mgmt_11.1.1/soa-infra-mgmt.jar
  • <mw_home>/Oracle_SOA/soa/modules/oracle.soa.fabric_11.1.1/oracle-soa-client-api.jar
  • <mw_home>/oracle_common/modules/oracle.fabriccommon_11.1.1/fabric-common.jar

我們程式的第一步需要透過Locator物件去執行Composite資料的查詢,Locator物件可以透過下列函式取得:

// 塞入WebLogic連接方式
Hashtable jndiProps = new Hashtable();
jndiProps.put(Context.PROVIDER_URL, 
              "t3://<host>:<port>/soa-infra");
jndiProps.put(Context.INITIAL_CONTEXT_FACTORY,
              "weblogic.jndi.WLInitialContextFactory");
jndiProps.put(Context.SECURITY_PRINCIPAL, "weblogic");
jndiProps.put(Context.SECURITY_CREDENTIALS, "<password>");
jndiProps.put("dedicated.connection", "true");
Locator locator = LocatorFactory.createLocator(jndiProps);




接下來,Locator需要透過一個"DN",識別你的Composite,舉例來說DN的格式如下:
blogdemo/CompositeDemo!1.0
其中blogdemo代表Composite被部署所在的partition名稱,"/"之後就是Composite的名稱 - CompositeDemo,最後在"!"之後則是此Composite的版本 - 1.0




查詢方式:


Composite composite = locator.lookupComposite("<dn>");




最終使用者需要查詢的是某個Composite instance狀態,這裡我們可以使用對應的CompositeInstance與CompositeIntanceFilter類別來做處理,下面是一個簡單的範例,我們需要找出所有正在執行中的Composite instance


// 產生CompositeIntance
CompositeInstanceFilter instanceFilter = new CompositeInstanceFilter();


// 指定查詢條件為正在執行的Composite intance
instanceFilter.setState(CompositeInstance.STATE_RUNNING);
List<CompositeInstance> instances = composite.getInstances(instanceFilter);
      
for (CompositeInstance ins : instances) {
  // 做你想做的吧!
}

Enjoy!!!

2011年3月29日 星期二

[Oracle BPM/SOA 實務] 隨意路由(Ad-hoc Routing) 的使用

最近剛好被問到這個問題,挖掘了一下發現BPM 11g的Human Workflow還有一些新功能能夠動態地讓使用者在人工關卡中邀請其他使用者審核,這樣的功能將讓Oracle BPM Suite在台灣這種強調【彈性】人工作業的環境能夠更加實用。

先來簡單看一下需求:
使用者需要在每一個人工作業關卡之中,根據需求與判斷決定是不是要在此關卡中拉進其他的人員來參與次關卡之簽核,拉進來可為一至多位,需支援多人的會簽與串簽的情境。
我們先簡單設計一個流程,其中只包含一個簡單的人工作業關卡與Exclusive Gateway。另外,也請為此關卡設計一個簡單的UI,如果感到陌生的話,請參考這裡的做法。


這裡的重點只有在其中Human Task的設定,接著在你的composite中點選你所開發的Human Task,請將你的Task Outcome給訂兩種結果:APPROVE與REJECT。

接下來重點在Task Assignment的設定,點選了Assignment Tab,你會看到類似下面的畫面:


Double-click 中間有"人像"的那個區域,這裡指定了此人工作業參與的人員設定,你可以看到這裡我直接指定此做頁是由 "tim" 使用者處理。


接下來是主要的部分了,我們要給予 "tim" 使用者在接到工作時,能夠將此工作分給其他的人員的權利。做法很簡單,你只要在同個視窗之中,將 Advanced 區域展開,並且點選 "Allow this participant to invite other participants" 就可以了。


按下 "OK" 回到上個畫面,接下來我們需要指定另一個規則:當有許多使用者被動態指定審核後,若有其中一個人REJECT了,其他平行被指定但尚未處理的工作將被自動取消。要做到這樣的需求,也是透過一個簡單的設定達成。

中央畫面右上角的 "Task will go from starting to final participant" 旁邊有支筆的圖案,請點選他,打開另一個設定視窗。


注意我們勾選了 "Complete task when a participant chooses: REJECT" 與 "Enable early completion in parallel subtasks",畫面中間那一支筆的圖案則是讓你選擇這種自動提前完成的作業是針對哪一種Outcome,這裡我們是針對有人REJECT的時候才處理,因此選擇REJECT。

接下來你就可以部署並測試你的流程了!

當你啟動一個流程instance後,使用 "tim" 使用者登入BPM Workspace,你會看到已經有個人工單指派給你。


當 "tim" 需要指定一個隨意路由時,他只需要選擇 "動作" --> "隨意路由",並可以看到一個對話框出現。



這對話框可以讓使用者決定他是要指定單一使用者審核還是多使用者,串簽方式還是會簽:

  • 單一核准者:就指定一個人啦!
  • 群組表決:會簽
  • 單一核准者鏈結:串簽
所有的使用者資訊可以透過下方的區域進行搜尋與指定,下圖示一個簡單的範例指定 "david" 與 "leon" 兩個使用者串簽。


至於會簽,Oracle BPM Suite其實是透過了功能更強的群組投票的方式來達成,在指定群組表決時,可以設定預設結果的百分比,例如,我們需要全部被指定人都ACCEPT才代表ACCEPT時,請將預設結果設定為 "核准"、共識百分比設定為 "100";若你需要的是五個人其中四個ACCEPT即可,你就需要設定共識百分比為 "80"。


請注意,這類的隨意路由在所有被指定使用者完成之後,系統就會根據結果直接完成此人工作業,並不會回到啟動隨意路由的人身上。如果你需要該啟動作做最後的同意,請他記得將自己加入路由的最後吧!

至於怎麼看到我送出的隨意路由的執行狀況,我們以一個會簽的流程為例,"tim" 在送出會簽邀請給 "david" 和 "leon" 之後,你會發現, "tim"本身已經沒有被指派的工作了,要查詢他所送出的工作你需要將畫面中央的 "指派對象" 從 "我和群組" 改選為 "上一個",你會發現,原來的工作現在另外產生了兩個子工作,分別被指派給 "david" 與 "leon" 兩人,你可以點選任意一個工作,並將工作內容中的 "歷史紀錄" 部分展開,你可以看到如下圖畫面所示的工作歷史。



另一種情境,使用者可能需要其他人給針對這件事情給一些意見,但是還是由自己本人做決定,在這樣的需求下,你可以使用 "動作" --> "要求資訊"。 這樣,被你所選定的使用者就只能提供一些註解或附件資訊,完成後工作還是回到原先使用者身上做決定。

自己試試看吧! Enjoy it!

2011年3月24日 星期四

[初探JavaEE6] 使用Eclipse與GlassFish開發JEE6應用程式 (Part II -- HttpServlet & RESTful Service)

HttpServlet開發

接下來,我們來看一下前端的網頁程式怎麼做吧,撇開始用複雜的web framework,這裡指先做一個HttpServlet的範例,我們用它來看看JavaEE 6做了些甚麼更動。

熟悉Eclipse的朋友們,產生Servlet的方式還是一樣,右鍵Project --> New --> Servlet,我們產生一個叫做MasterServlet的Servlet出來。



當結束後,Eclipsse自動幫你產生了下列程式碼,

package blogjee6;

import java.io.IOException;
...

/**
 * Servlet implementation class MasterServlet
 */
@WebServlet("/MasterServlet")
public class MasterServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;
  
  /**
   * @see HttpServlet#HttpServlet()
   */
  public MasterServlet() {
    super();
    // TODO Auto-generated constructor stub
  }

  /**
   * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
   */
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
  }
}

請注意,在class上面有個新的annotation叫做@WebServlet,JavaEE 6的想法是多利用annotation的方式將資訊可以附加在程式碼之中,@WebServlet就是用來指定此HttpServlet所接受HTTP request的URI資訊。你可能懷疑,那傳統的web.xml到哪裡去了呢? 的確,再產生了這個HttpServlet之後,你到WEB-INF下面看一下,Eclipse現在不會自動幫你產生對應的web.xml以及其中的Servlet設定資料,一切資料現在都在class的annotation之中了!

但web.xml並不是就此失效或不再使用,其實,你還是可以使用web.xml的傳統方式來去指定,指示通常是在deploy階段有需要特定的更動才會使用。

我們需要呼叫前一篇文章所產生的EJB做資料查詢,首先,我們需要container能夠幫我們inject入此EJB instance,我們只需要在MasterServlet的property中加入此行:

@EJB MasterFacade info;

接著在doGet method之中加入下列程式碼以產生網頁:

Master master = info.queryMaster(1L);
PrintWriter out = response.getWriter();

out.println("<html><body>");
out.println("<h1>");
out.println(master.getMasterId() + ", " + master.getCol1() + ", " + master.getCol2());
out.println("</h1>");
List<Detail> details = master.getDetails();
for (Detail detail : details) {
  out.println("<h2>");
  out.println(detail.getDetailId() + ", " + detail.getCol1());
  out.println("</h2>");
}
out.println("</body></html>");


老實說,少了context setup,interface lookup...等事情,就這樣直接當作POJO使用,是比以前爽度提升太多了...

連接到http://localhost:8080/BlogJEE6/MasterServlet,你應該可以看到你在MASTER與DETAIL資料庫table中的資料。


RESTful Service開發

系統與功能的開發,通常會伴隨著給人看的介面以及給其他系統進行資料交換的介面,近年來在企業內部遠端功能呼叫或是資料查詢,通常是使用Web Service的方式以SOAP (舊名為Simple Object Access Protocol) 訊息做交換,SOAP一方面是標準,一方面也相當成熟有一堆相關的標準去支援安全性、交易一制性...等的功能。只是... SOAP可以一點都不simple,相對的實在是有點肥,也因此,很多人都使用HTTP + XML/JSON的方式做自己的資料介面,RESTful就算是這一類的做法吧。

首先,你需要在Project Facet中多選個JAX-RS。右鍵Project --> Properties 再選Project Facet就可,記得,當你勾選時,下方會有紅色的Further configuration選項讓你進行細部設定。


在這裡,我把JAX-RS Servlet的URL mapping指定為/rest/*,代表所有在/rest/之下的URI代表就是RESTful Service。



我們想產生一個查詢Master與附屬Detail資料的RESTful Service,回傳的資料以XML的格式做為表示,我們可以在Eclipse中右鍵Project --> RESTful Web Service from Pattern (Java EE 6),並在對話框中輸入對應的資料:


自動產生的程式碼如下:


package blogjee6;


import javax.ws.rs.Consumes;
...


@Path("generic")
public class MasterResource {
  @SuppressWarnings("unused")
  @Context
  private UriInfo context;


  /**
   * Default constructor. 
   */
  public MasterResource() {
    // TODO Auto-generated constructor stub
  }


  /**
   * Retrieves representation of an instance of MasterResource
   * @return an instance of String
   */
  @GET
  @Produces("application/xml")
  public String getXml() {
    // TODO return proper representation object
    throw new UnsupportedOperationException();
  }


  /**
   * PUT method for updating or creating an instance of MasterResource
   * @param content representation for the resource
   * @return an HTTP response with content of the updated or created resource.
   */
  @PUT
  @Consumes("application/xml")
  public void putXml(String content) {
  }
}


我們希望的是其他的應用程式可以透過某特定的URI去查特定Master資料,例如:透過/master/1我們可以查到id為1的Master與對應Detail資料。這個資訊我們可以透過@Path("/master_old/{masterId}"),以@Path annotation指定URI資訊,另外在中間放一個{masterId}的"參數",以方便動態帶入不同的查詢條件。

而在getXml() method之前,有兩個annotation @GET@Produces,分別代表此method所處理的HTTP request以及回傳的MIME-type。

為了我們的需求,我們需要做一些簡單的修改:

@GET
@Produces("application/xml")
public String getXml(@PathParam("masterId") String masterId) {


這裡getXml() method多了一個masterId input String,我們在這個String之前加了一個annotation @PathParam("masterId"),代表了這個String資料會來自於@Path之中{masterId}"參數"。

下來就是填空了,把getXml()的內容透過呼叫MasterFacade EJB完成。一樣的,我們需先宣告一個MasterFacade讓container來inject。

@EJB MasterFacade info;

將下列的code加入getXml()之中:
String result = "";
Master master = null;

if (masterId != null && !"".equals(masterId)) {
  master = info.queryMaster(Long.parseLong(masterId));
}

if (master != null) {
  result += "<master>";
  result += "<master_id>" + master.getMasterId() + "</master_id>";
  result += "<col1>" + master.getCol1() + "</col1>";
  result += "<col2>" + master.getCol2() + "</col2>";
  
  if (master.getDetails().size() > 0) {
    result += "<details>";
    for (Detail detail : master.getDetails()) {
      result += "<detail_id>" + detail.getDetailId() + "</detail_id>";
      result += "<col1>" + detail.getCol1() + "</col1>";
    }
    result += "</details>";
  }
  result += "</master>";
}
return result;


最後,有一件事情,你需要在MasterRource class之前在加上一個annotation @Stateless,用意是讓container 能夠管這個class,也讓你的class呼叫EJB才不會出問題。

連到http://localhost:8080/BlogJEE6/rest/master/1試試看吧,你應該可以看到XML資料了!




2011年3月10日 星期四

[初探JavaEE6] 使用Eclipse與GlassFish開發JEE6應用程式 (Part I -- JPA & EJB)

JavaEE 6標準已經完成一陣子了,現在JavaEE 7也開始起跑,只是這幾年來似乎JavaEE 6標準的提升似乎並沒有吸引了太多台灣開發人員的注目,畢竟之前J2EE 1.3、J2EE 1.4至JavaEE 5改版對於多數人的開發來說,差異性沒有太大 (EJB導入的比例太小也是其中原因之一),大部分的開發人員都傾向擁抱比較light-weight的開發framework與平台,導致Spring + Hibernate現在仍舊是主流技術。

為什麼我會突然開始關心起JavaEE 6呢?

實在是因為這次JavaEE 6所包裝的一些技術與標準讓我覺得 -- 也許JavaEE 6會是讓開發人員開始考慮回歸JavaEE 6標準主流的一個轉機。幾個吸引我的主題包括:模組化設計(Web Profile)的設計、CDI的引進、大量使用annotation開發、廢除EntityBean使用JPA以及JAX-RS Restful Service的標準。

我的想法是,試著開發出一個簡單的應用場景,看看使用JavaEE 6對開發人員是否有一些吸引力。我將場景設定為:使用EJB開發一個Business Object,其中應用JPA將資料庫中兩個table的一對多資料抓出;同時,為此EJB提供對外兩種介面:HttpServlet網頁的顯示與Restful Service應用程式查詢介面。

開發環境準備


這邊我所使用的開發工具與伺服器是Eclipse + GlassFish,如果你想要省事的話,可以到Oracle Enterprise Pack for Eclipse的網站下載最新版本的開發工具,其中已經將Eclipse與GlassFish Tools plug-in安裝預設安裝好。然後在這邊下載GlassFish 3.1版。
註:這篇不是Eclipse教學,我不會說明怎麼安裝Eclipse、設定Server...等,網路上已經有夠多的文件,有需要的人可以拜一下孤狗大神
另外,我選用Oracle資料庫做為資料儲存的地方,你可以用下列DDL產生兩個所開發需要的table:

CREATE TABLE MASTER (
MASTER_ID NUMBER(22 , 0) NOT NULL,
COL1 VARCHAR2(30),
COL2 VARCHAR2(30));
ALTER TABLE MASTER ADD CONSTRAINT PK_MASTER PRIMARY KEY (MASTER_ID);



CREATE TABLE DETAIL (
DETAIL_ID NUMBER(22 , 0) NOT NULL,
MASTER_ID NUMBER(22 , 0),
COL1 VARCHAR2(30));
ALTER TABLE DETAIL ADD CONSTRAINT PK_DETAIL PRIMARY KEY (DETAIL_ID);
ALTER TABLE DETAIL ADD CONSTRAINT FK_MASTER FOREIGN KEY (MASTER_ID) REFERENCES MASTER (MASTER_ID);