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);

2011年3月1日 星期二

[Oracle SOA 應用] 使用Mediator與Event處理content-based routing

這幾天剛好有個朋友問到一種在SOA設計上面常遇到的情境,使用SOA (ESB)架構做request 的content-based routing設計,常常設計出下列的composite:


這樣的設計是由SOA平台對外提供單一介面,然後根據傳入訊息內容決定要呼叫哪一個End-Point Web Service。

在SOA 11g的SCA架構中,通常是把相對應的end-point,中間的mediator與對外的web service打包成一個composite,然後一次部署上server。這樣的設計在一般情境下都非常適用,開發人員只要拖拉點選就可以快速完成。只不過,要是需求的end-point有可能有上百個的話,這張圖可能就太大了,而每一次的重新部署都會影響到所有的服務,相對應的在維護上反而造成了小小的不便。

要怎麼樣解決這樣的問題哩? 我建議可以從幾種方式下手: