SOA 베스트 프랙티스: The BPEL Cookbook

BPEL 프로세스의 다이내믹 생성
저자 Jerry Thomas

XQuery를 통해 데이터베이스에 저장된 매개변수를 BPEL XML 정의로 변환하고 BPEL 프로세스를 다이내믹하게 생성하는 방법을 설명합니다.

전체 BPEL Cookbook 시리즈 목록 보기

아티클 관련 다운로드:
샘플 코드
Oracle BPEL Process Manager and Designer
Saxon XSLT and XQuery Parser

게시일: 2005년 11월

이 시리즈의 제 3부에서는, 엔드 포인트 레퍼런스(end point reference)를 통해 파트너 링크를 처리함으로써 BPEL 프로세스를 다이내믹하게 생성하는 방법에 대해 알아 보았습니다. 이러한 방법은 파트너와의 관계에 변화가 발생하더라도, 이에 무관하게 프로세스를 구축할 수 있다는 장점이 있습니다. 하지만, 파트너 링크 이외의 다른 변수들을 다이내믹하게 변경해 주어야 하는 상황이 있을 수도 있습니다.

기업의 부동산을 관리하고, 아래와 같은 클라이언트의 질문에 답변하기 위한 작업장 관리 솔루션(workspace management solution)을 설계하는 경우를 생각해 봅시다.
  • 각 비즈니스 부서 별로 얼마나 많은 공간을 사용하고 있는가?
    비즈니스 부서들이 공간을 효율적으로 공유하려면 어떻게 해야 하는가?
  • 부동산 임대가 언제 만료되는가? 기업의 성장 곡선을 감안할 때, 재협상 시 임대 비용은 얼마 정도가 적정한가?
  • 비즈니스 부서를 다른 층, 또는 다른 건물로 이전하는 경우 최적의 계획은 무엇인가?
작업장 관리 솔루션은 사무실 큐브(cube) 공간의 점유 상태를 추적하고, 3개월 이상 큐브가 비어 있는 경우 해당 공간을 부서로부터 할당 해제하는 등의 작업을 자동화합니다. 이 프로세스를 여러 클라이언트에 적용하는 과정에서, 각 클라이언트에 맞춤화된 프로세스를 제공할 필요성이 대두되었습니다. 어떤 기업은 큐브를 할당 해제하기 전에 자산 관리자에게 이메일을 보내기를 원할 수 있고, 또 다른 기업은 조치를 취하기 전에 충분한 유예 기간을 주기를 원할 수도 있습니다.

하지만 각 클라이언트 별로 비즈니스 프로세스를 생성하는 것은 시간, 비용, 리소스 면에서 매우 많은 투자를 필요로 합니다. 또 비즈니스 프로세스를 재활용하려면 BPEL에 대한 매우 깊은 지식이 요구됩니다.

이에 반해, 어댑티브 비즈니스 프로세스(adaptive business process)는 각 조직의 요구사항에 대해 신속한 커스터마이즈가 가능하고, 신속한 자동화가 가능하며, 개발 비용이 절감될 뿐 아니라 타임 투 마켓(time to market)을 가속화하는 효과를 제공합니다. 기업은 이러한 접근 방법을 사용함으로써 BPEL 환경의 복잡성을 최소화하고 “실제 환경”과 가장 유사한 비즈니스 프로세스 모델을 구현할 수 있습니다.

하지만, 어댑티브 비즈니스 프로세스는 애널리스트가 비즈니스 플로우를 수정하기 위한 커스텀 비즈니스 프로세스 디자이너(custom business process designer)를 필요로 합니다. 커스텀 디자이너는 프로세스의 정의를 데이터베이스에 저장하고 관리합니다. 프로세스의 업데이트가 필요할 때마다, 비즈니스 애널리스트는 커스텀 디자이너를 사용하여 프로세스를 변경합니다.

일단 프로세스 정의가 데이터베이스에 로딩되면, BPEL 프로세스를 다이내믹하게 생성하는 것이 가능합니다. 이렇게 생성된 비즈니스 프로세스는 또한 다이내믹하게 deploy됩니다.

Oracle BPEL Process Manager는 이러한 접근법을 위한 이상적인 툴로 활용됩니다. Oracle BPEL Process Manager는 써드 파티 디자이너와 원활하게 연동할 뿐 아니라, BPEL 프로세스의 다이내믹 생성을 위한 기능을 제공합니다. 실제로 필자가 근무하는 CenterStone Software는 이러한 방법을 사용하여 작업장 관리 및 부동산/시설 관리를 자동화하기 위한 솔루션을 개발하였습니다. 우리가 구축한 eCenterOne 애플리케이션은 Oracle BPEL을 기반으로 시설 관리자들이 커스텀 비즈니스 프로세스를 신속하게 개발하고 구축하기 위한 환경을 제공하고 있습니다.

본 문서에서는, 커스텀 BPEL 디자이너를 이용해 데이터베이스에 프로세스 정의를 저장하는 방법에 대해 설명합니다. 먼저 프로세스 정의를 저장하기 위한 데이터베이스 스키마를 검토한 후, XQuery와 Saxon XQuery parser를 이용하여 샘플 비즈니스 프로세스로부터 BPEL 파일을 다이내믹하게 생성하는 방법을 예시합니다. 그리고 마지막으로, Ant 스크립트를 통해 프로세스를 다이내믹하게 deploy하는 방법을 배워보기로 합니다.

접근 방법

앞에서 설명한 것처럼, 프로세스를 다이내믹하게 생성하는 기능은 매우 중요한 혜택을 제공합니다. 그림 1은 다이내믹하게 생성된 비즈니스 프로세스의 라이프사이클을 보여주고 있습니다.

figure 1
그림 1 비즈니스 프로세스 라이프사이클

다이내믹하게 “on-the-fly” 프로세스를 생성하기 위해서는 여러 단계의 작업이 필요합니다. 먼저 애널리스트는 커스텀 디자이너를 이용하여 비즈니스 프로세스를 비주얼하게 모델링합니다.

  1. 프로세스 정의는 커스텀 디자이너에 의해 데이터베이스에 저장됩니다.
  2. BPEL Generator는 프로세스 정의를 읽고 검증합니다. 그런 다음 데이터베이스에 저장된 데이터를 이용하여 BPEL XML 파일과 기타 필요한 파일을 생성합니다.
  3. BPEL 프로세스는 Oracle BPEL Process Manager Server에 다이내믹하게 deploy됩니다.
샘플 비즈니스 시나리오를 통해 각 단계를 설명하기로 하겠습니다.

샘플 비즈니스 시나리오

앞에서 설명한 작업장 관리 비즈니스 프로세스를 예로 들어 설명하겠습니다. “Cube Management” 비즈니스 프로세스는 다음과 같은 액티비티로 구성됩니다:
  1. 사무실의 큐브(cube)가 빈 경우 비즈니스 프로세스를 시작합니다 (해당 큐브의 EmployeeCount가 0으로 설정됩니다).
  2. 3 개월 간 유예 기간을 둡니다 (EmployeeCount가 0 보다 높은 값으로 재설정되는 경우 플로우를 종료합니다).
  3. 자산 관리자에게 이메일을 전송하여, 해당 공간이 더 이상 기존의 비즈니스 부서에 할당되어 있지 않음을 통보합니다.
  4. 해당 공간의 데이터를 업데이트하여, 기존 비즈니스 부서에 할당된 내역을 해제합니다.
프로세스의 생성. 가장 먼저 프로세스 생성 작업이 수행됩니다. 일반적으로, 프로세스는 Oracle Designer 또는 Eclipse Designer를 이용하여 모델링 되며, BPEL 파일은 자동으로 생성됩니다.

예제에서는 일반적으로 사용되는 IDE를 커스텀 디자이너로 대체하기로 합니다. 또 BPEL 파일을 직접 생성하는 대신, 데이터베이스에 정의를 저장하는 방식을 사용합니다.

그림 2는 샘플 비즈니스 프로세스를 디자이너에서 모델링하는 과정을 보여 주고 있습니다.

figure 2
그림 2 커스텀 프로세스 디자이너의 Cube Management 비즈니스 프로세스

앞에서 설명한 것처럼, Oracle BPEL Process Manager 엔진은 써드 파티 디자이너/툴과의 통합 기능을 제공합니다. 또 써드 파티 디자이너/툴을 이용하여 BPEL 파일을 다이내믹하게 생성할 수 있습니다.

데이터베이스에 프로세스 정의 저장. 비즈니스 프로세스가 생성되면, 다음에는 데이터베이스에 프로세스 정의를 저장하는 작업이 수행됩니다. 데이터베이스 스키마는 복잡한 프로세스를 지원할 수 있는 유연성이 제공되어야 하며, flow, pick, invoke, scope 등의 다양한 BPEL 컨스트럭트를 이용한 BPEL 프로세스의 재생성을 지원해야 합니다.

고객이 필요로 하는 비즈니스 프로세스 디자인 정보를 포함하는 데이터 모델을 설계해 두었다고 가정해 봅시다. 비즈니스 애널리스트가 디자이너를 이용하여 비즈니스 프로세스를 생성하면, 비즈니스 프로세스의 구조를 정의한 정보는 데이터베이스에 저장됩니다. deploy 작업이 시작되기 전까지는 Oracle BPEL Process Manager가 사용되지 않음을 참고하시기 바랍니다 (여기에 대해서는 뒷부분에서 자세히 설명합니다).

그림 3은 데이터베이스에 프로세스 설계 내역을 정의하는데 사용되는 데이터 모델의 하이 레벨 UML 뷰를 보여주고 있습니다.

figure 3
그림 3 Cube Management 프로세스 정의의 저장을 위한 단순화된 데이터 스키마

The WorkflowProcess 오브젝트는 프로세스 설계 내용을 저장하는데 사용됩니다. 이 오브젝트에 포함된 매개변수와 노드는 프로세스의 시퀀스와 액티비티 타입을 정의합니다. 디자이너가 지원하는 프로세스 액티비티 타입에 따라 각각 다른 WorkflowNode 오브젝트가 사용됩니다. 이 오브젝트가 BPEL 컨스트럭트가 아니며, 하나 이상의 BPEL 액티비티에 매핑되는 커스텀 컨스트럭트임을 참고하시기 바랍니다. 커스텀 디자이너를 통해 deploy 작업이 이루어지고 나면 CubeManagement 프로세스의 상세 정보는 아래와 같이 데이터베이스에 저장됩니다.

WorkflowProcess 테이블

Name

ID

CubeManagement

1

WorkflowParameter 테이블

WorkflowProcess_ID

Name

Type

1

CubeParam

Space

WorkflowNode 테이블

Workflow
Process_ID

NodeId

NextNode

Type

E-mail

Parameter_ID

UpdateField

UpdateValue

WaitTime

1

1

2

Start






1

2

3

Wait





3 months

1

3

4

Message

max@big.com





1

4

5

Update


1

businessUnit

null


1

5

Null

End






BPEL Generator는 이 테이블을 읽어 들여 BPEL 파일을 다이내믹하게 생성하기 위해 사용합니다. 아래 섹션은 이 프로세스를 설명하고 있습니다.

BPEL의 다이내믹 Deploy 작업

데이터베이스로부터 프로세스 설계내역을 읽어 들이고 맞춤화된 BPEL 프로세스를 생성하는 것은 BPEL 생성 과정에서 가장 복잡한 작업입니다. 본 예제에서는 XQuery를 이용하여 BPEL을 생성하게 됩니다.

XQuery는 XML 데이터 처리를 위한 강력하고 편리한 언어입니다. XQuery는 XML 포맷의 파일 뿐 아니라 XML과 유사한 구조(nested, name trees, attribute 등)를 갖는 데이터베이스 데이터 또한 지원합니다.

Oracle BPEL Process Manager에서 사용하기 위해 다이내믹하게 생성되는 파일의 목록이 아래와 같습니다.

BPEL 파일

실제 비즈니스 프로세스를 정의한 XML 파일로, BPEL 스펙을 따릅니다.

WSDL 파일

Web Service WSDL 파일은 두 가지 인터페이스를 기술합니다:

  • Oracle BPEL Process Manager에서 사용되는 BPEL 프로세스에 의해 공개되는 인터페이스. 이 인터페이스는 비교적 단순하며, 프로세스 인스턴스를 시작하기 위한 호출 및 콜백 인터페이스를 포함하고 있습니다.

  • • eCenterOne에 의해 공개되는 인터페이스. 단순성을 위해, 여기에서도 Oracle BPEL Process Manager에서 실행되는 프로세스로부터 발생하는 모든 호출 및 관련 콜백(callback)을 처리할 수 있는 단일 인터페이스가 사용됩니다.

bpel.xml

Oracle BPEL Process Manager에서 BPEL 프로세스의 기술을 위해 사용하는 deployment descriptor XML 파일.

이 파일은 Oracle BPEL Process Manager가 내부적으로 필요로 하는 정보 이외에도 다음과 같은 데이터를 포함하고 있습니다.

  • eCenterOne 웹 서비스의 위치를 명시한 위치 정

  • eCenterOne에 대한 호출이 실패한 경우 작업이 어떻게 재시도 되어야 하는지에 대한 정보.

Ant build.xml

Oracle BPEL Process Manager의 bpelc Ant 태스크를 이용하여 BPEL 프로세스를 deploy합니다 (뒷부분에서 자세하게 설명)

여기에서는 BPEL 파일을 다이내믹하게 생성하는 과정에만 초점을 맞추기로 합니다. 이 프로세스에는 데이터베이스에 저장된 노드들을 읽어 들이고, 각 노드 별로 해당되는 BPEL 액티비티를 생성하는 작업이 수반됩니다:
  1. 데이터베이스 노드의 조회.
  2. BPEL 생성 과정은 Java 코드를 통해 구현됩니다. Java 코드를 이용해 각 워크플로우 프로세스를 위한 WorkflowNode를 읽어 들이고, 처리되어야 하는 모든 노드의 목록을 생성합니다.
  3. 각 노드를 위한 BPEL 액티비티의 생성.
  4. 목록에 포함된 각 워크플로우 노드로부터 <scope/> 액티비티 태그가 적용된 BPEL 액티비티가 생성됩니다. BPEL 액티비티의 처리 과정에서 BPEL 매개변수가 필요한 경우, 해당 매개변수는 해당 시점의 스코프에서만 확인이 가능합니다 (따라서 변수명 충돌과 같은 문제를 예방할 수 있습니다). 각 노드에 대해 XQuery 템플릿을 생성하고 BPEL XML 태그를 생성해야 합니다. XQuery 프로세서는 XQuery 템플릿을 처리하여 BPEL 노드 및 인스턴스 정보를 구성하고, 그 결과를 다시 반환합니다.
  5. BPEL 파일의 생성.
  6. 호출 프로세스는 각 노드를 위한 BPEL 액티비티를 조합하여, 전체 프로세스를 대표하는 마스터 BPEL 파일을 생성합니다. 또 각각의 WokflowNode를 위한 <scope/> 블록이 생성되어 첨부됩니다.
<process>
	<scope>
		<!— start workflow node related activities -!>
	</scope>
	<scope>
		<!— wait workflow node related activities -!>
	</scope>
	<scope>
		<!— mail workflow node related activities -!>
	</scope>
	<scope>
		<!— update workflow node related activities -!>
	</scope>
</process>
위의 2번째 단계를 좀 더 상세히 살펴보기로 하겠습니다. 각각의 노드는 일련의 BPEL 액티비티에 대응되며, 모든 정보는 XQuery 템플릿 파일에 저장됩니다. 각 노드 별로 XQuery 템플릿이 존재하며, 이 템플릿은 해당 노드를 위한 모든 BPEL 컨스트럭트를 포함하고 있습니다. 이를 위해 Cube Management 프로세스의 5개 노드에 대응되는 start.xq, wait.xq, message.xq, update.xq, stop.xq를 생성하였습니다.

아래 표는 예제 프로세스의 start, wait, message, update, stop 노드를 위해 생성된 BPEL 액티비티를 설명하고 있습니다.

Cube Management
프로세스의 노드

대응 BPEL 컨스트럭트

Start

<sequence>






<receive>





<assign>





<scope>






<variables>





<sequence>






<assign>





<invoke>

Wait

<scope>






<variables>





<sequence>






<assign>





<invoke>





<pick>


message

<scope>






<variables>





<sequence>






<assign>





<invoke>


update

<scope>






<variables>





<sequence>






<assign>





<invoke>


End

<scope>






<variables>





<sequence>






<assign>





<invoke>





<terminate>


각각의 프로세스 노드가 BPEL 액티비티의 계위 내에 생성되는 과정을 눈 여겨 보시기 바랍니다. 한 예로, message 액티비티를 생성하기 위해 사용되는 BPEL이 아래와 같습니다. 이 정보는 message.xq XQuery 파일에 저장됩니다.

declare namespace ent = "http://www.centerstonesoft.com/schema/entity"; declare variable $nodeKey as xs:string external;
<scope>
	<!-- generation for Message activity -->
	<variables>

	<!-- Define variables input, output and error variables-->
		<variable name="emailRequest" messageType="services:EntityAsyncMsgWSRequestMessage"/>
		<variable name="emailResponse" messageType="services:EntityAsyncMsgWSResultMessage"/>
		<variable name="fault" messageType="services:EntityFaultMessage" />
	</variables>

	<!--Begin the BPEL activities -->
	<sequence>

	<!--This is the first assign activity. It configures the input variable with 
   <!-- identification of node ($nodekey) and function to be called -->
	<!-- by the external webService (bpelSendEmail). -->

		<assign>
			<copy>
				<from>
					<ent:EntityCollectionElem xmlns:ent="http://www.centerstonesoft.com/schema/entity" >
						<ent:Header/>
						<ent:entityName/>
						<ent:EntityElem>
							<ent:entityKey>{ $nodeKey }</ent:entityKey>
							<ent:method >
								<ent:methodName>bpelSendEmail</ent:methodName>
								<ent:parameter>
									<ent:PropName>thread</ent:PropName>
									<ent:Type>string</ent:Type>
									<ent:Value/>
								</ent:parameter>
							</ent:method>
						</ent:EntityElem>
					</ent:EntityCollectionElem>
				</from>
				<to variable="emailRequest" part="payload"/>
			</copy>
		</assign>

	<!--Second and third assign activity configure the input variable -->
	<!--with identification of actual process instance -->

		<assign>
			<copy>
				<from variable="input" part="parameters" query="//ent:Header"/>
				<to variable="emailRequest" part="payload" query="//ent:Header"/>
			</copy>
		</assign>
		<assign>
			<copy>
				<from variable="threadKey" part="value"/>
				<to variable="emailRequest" part="payload" query="//ent:parameter[ent:PropName = 'thread']/ent:Value"/>
			</copy>
		</assign>

		<!--After configuring input variable with node, instance and function to be called, 
		       invoke the external service -->
		<invoke name="invoke" partnerLink="EntityAsyncMsgWSLink" 
		           portType="services:EntityAsyncMsgWS" operation="invoke" inputVariable="emailRequest"/>

		<!--Check response from external service. If response indicates fault, throw exception. -->
		<!--If no response is received for 10 days, throw exception -->
		<pick name="deadlined_receive" >

			<!--Set output variable if return result from external webservice is OK-->
			<onMessage partnerLink="EntityAsyncMsgWSLink" portType="services:EntityAsyncMsgWSCallback" 
			      operation="onResult" variable="emailResponse">
				<empty/>
			</onMessage>

		<!--On fault, check the fault code and throw  appropriate exception-->

			<onMessage operation="onFault" partnerLink="EntityAsyncMsgWSLink" 
			      portType="services:EntityAsyncMsgWSCallback" variable="fault">
				<switch>
					<case condition="bpws:getVariableData('fault', 'value',  
					      '/EntityFault/faultcode') = 'MailException' ">
						<throw faultName="ent:MailException" faultVariable="fault" />
					</case>
					<otherwise>
						<throw faultName="ent:SystemException" faultVariable="fault" />
					</otherwise>
				</switch>
			</onMessage>

			<!--If no response for 10 days, throw  exception-->
			<onAlarm  for="'P10D'">
				<throw faultName="ent:SystemException"/>
			</onAlarm>
		</pick>
	</sequence>
</scope>
XQuery 템플릿을 살펴보면, message 프로세스 노드를 포함하는 단일 <scope> 액티비티가 어떻게 생성되는지 확인할 수 있을 것입니다. <scope> 액티비티는 <variables>섹션으로 시작됩니다.<variables>섹션은 요청 및 정상/장애 상황의 응답을 위한 변수가 정의됩니다. <sequence> 섹션 이후의 변수 선언 부분에서는 일련의 <assign> 구문을 통해 요청이 생성됩니다. 첫 번째 <assign> 은 요청에서 반복적으로 사용되는 부분을 작성하는데 사용됩니다. 이때, nodeKey XQuery 매개변수를 사용하여 노드 ID를 외부 웹 서비스에 전달하게 됩니다.

nodeKey 는 생성 시간 정보를 기준으로 설정됩니다. 첫 번째 <assign> 액티비티 이후 수행되는 2 개의 <assign> 액티비티는 런타임 정보를 처리하게 됩니다. 두 번째 <assign> 액티비티는 프로세스를 시작하는데 사용된 최초 요청으로부터 헤더 정보를 복사합니다. 이 헤더 정보에는 eCenterOne과 BPEL Process Manager 프로세스 ID와 프로세스가 적용되는 고객을 식별하기 위한 정보가 포함되어 있습니다. 세 번째 <assign> 액티비티는 프로세스 쓰레드 ID를 복사합니다. (앞에서 설명한 것처럼 프로세스의 각 노드 별로 다르지만 유사한 XQuery 템플릿이 존재합니다.)

XQuery 프로세싱. 이 템플릿들이 XQuery 프로세서에 의해 어떻게 처리되고 하나의 BPEL 파일로 조합되는지 간단하게 설명해 보겠습니다.

아래의 generateActivity() 는 BPEL 액티비티 생성이 필요한 노드의 목록을 가져 옵니다. 그런 다음, XQuery.newXQueryFromResource. 함수를 이용하여 각각의 노드에 해당되는 XQuery 템플릿을 확인합니다. Saxon 프로세스는 이 XQuery 템플릿을 처리하여 XML 문자열을 생성합니다. 노드에 자식 노드가 존재하는 경우, 이 작업은 while 루프를 통해 재귀적인 형태로 반복됩니다.
/**
 * Recursively generates an activity including any child activities.
 * @param activity the node that is being generated, the value is null for the top-level
 * process node
 * @return typically the root element of the generated XML document
 * @throws EntityException
 */
    protected String generateActivity(WorkflowNode activity) throws EntityException
    {
	//Find the next node in the list to be processed
        BpelActivityType activityType = getBpeBpelActivityType(activity);
	//Get the parameters to be passed to node
        HashMap params = calculateParameters(activity, activityType);
        try {
            //Get corresponding XQuery file for the node
            XQuery q = XQuery.newXQueryFromResource("bpelgen/" + activityType.getQuery());
            //Call the Saxon XQuery Processor to run the query. 
            XQueryResult result = q.run(null, params, null);
            //Convert the result into a string 
            String parentXml = result.serializeString();
            if (logger.isDebugEnabled()) {
                logger.debug("Generated for parent '" + activityType.getName() + "' is \n" + parentXml);
            }
            //If there are child nodes, recursively invoke generate activity
            if (_process.hasChildren(activity)) {
                //Call java relational class
                Iterator it = _process.getChildrenIterator(activity);
                //Recursively call generateActivity for child nodes. For ‘Cube Management Process’, these will be 
	//start, wait, message, update & end
                while (it.hasNext()) {
                    WorkflowNode childActivity = (WorkflowNode) it.next();
                    String childXml = generateActivity(childActivity);
                    // now use Saxon XSLT to add the document generated for the children to the result document
	// Get the Transformer used for this transformation
                    Transformer transformer = getPrependTransformer();
                    transformer.setParameter("inserted", DomUtils.getDocument(new ByteArrayInputStream(childXml.getBytes())));
                    StreamResult saveResult = new StreamResult(new ByteArrayOutputStream());
                    try {
                        StreamSource src = new StreamSource(new ByteArrayInputStream(parentXml.getBytes()));
	   // Transform the XML Source
                        transformer.transform(src, saveResult);
                    }
                    finally {
                        transformer.clearParameters();
                    }
                    parentXml = ((ByteArrayOutputStream)saveResult.getOutputStream()).toString();
                    if (logger.isDebugEnabled()) {
                        BpelActivityType childType = getBpeBpelActivityType(childActivity);
                        logger.debug("Preprocessed for parent '" + activityType.getName() + "' and child '" +
                                     childType.getName() + "' is \n" + parentXml);
                    }
                }
            }
            // remove the <insert-here> node
            Transformer transformer = getRemovePrependTransformer();
            StreamResult saveResult = new StreamResult(new ByteArrayOutputStream());
            StreamSource src = new StreamSource(new ByteArrayInputStream(parentXml.getBytes()));
            transformer.transform(src, saveResult);
            parentXml = ((ByteArrayOutputStream)saveResult.getOutputStream()).toString();
            if (logger.isDebugEnabled()) {
                logger.debug("Generated for '"+activityType.getName()+"' is \n" + parentXml);
            }
            return parentXml;
        }
        catch (SAXException ex) {
            throw new EntityException("Failed to run transform "+activityType.getQuery(), ex);
        }
        catch (TransformerException ex) {
            throw new EntityException("Failed to run transform "+activityType.getQuery(), ex);
        }
        catch (IOException ex) {
            throw new EntityException("Failed to load query "+activityType.getQuery(), ex);
        }
    }
The generateActivity() 메소드는 문자열 포맷으로 마지막 BPEL 파일을 생성합니다. 이렇게 생성된 데이터는 다른 Java 프로그램을 통해 파일로 저장됩니다. 샘플 코드 파일에서 Cube Management 프로세스를 위한 모든 XQuery 템플릿과 BPEL 파일을 다운로드하실 수 있습니다.

Ant를 이용한 다이내믹 deploy 작업. 마지막으로 Oracle BPEL Process Manager의 deploy 툴을 이용하여 BPEL 프로세스를 deploy하는 작업이 수행됩니다. 이때 아래와 같은 간단한 Ant buld.xml 파일을 생성하여 실행해야 합니다 (여기에서도 매개변수화된 XML 파일을 지원하기 위해 XQuery가 사용됩니다).
<?xml version="1.0" encoding="UTF-8"?>
<project default="main" basedir="C:/DOCUME~1/jerry/LOCALS~1/Temp/cs_bpelgen_simple24369" name="BpelDeploy">
   <target name="main">
      <bpelc keepGenerated="true" deploy="jt_dev" rev="1.0" home="c:\orabpel"/>
   </target>
</project>
마지막으로 애플리케이션 서버의 프로그램에서 Ant를 실행하는 작업이 남아 있습니다. 이를 위해 Ant의 Execute 태스크가 제공하는 코드를 사용할 수 있습니다 (org.apache.tools.ant.taskdefs 참고). 이 클래스는 커맨드 라인 프로세스를 프로그램적으로 실행하고 에러를 처리하기 위한 지원 기능을 제공합니다.

Ant Execute 태스크의 코드를 이용하여 BPEL 프로세스 deploy 작업을 구현하는 예제가 아래와 같습니다.
String args[] = new String[1];
args[0] = "c:/orabpelpm/integration/orabpel/bin/obant.bat";
            
Execute exe = new Execute();
exe.setCommandline(args);
exe.setWorkingDirectory(_deploydir);
int retval = exe.execute();
if ( retval != 0 ) {
    throw new ExecuteException("process failed to deploy"); 
위에서 Oracle BPEL Process Manager 커맨드 라인 파일 obant를 직접 실행하기 위해 Execute 클래스를 사용되고 있습니다. 먼저 Windows 환경을 설정하기 위한 셋업 작업이 실행되고 그런 다음 build.xml 파일 상에서 Ant가 호출됩니다.

BPEL 컴파일러에 의해 에러가 발생되는 경우, 0이 아닌 코드가 반환되게 됩니다. 이 에러는 그 빈도가 높지는 않으며, 대부분의 경우 프로세스 구현 과정에서의 내부 에러로 인해 발생합니다.

결론

CenterStone Software는 커스텀 디자이너를 이용하여 커스터마이즈된 비즈니스 프로세스를 다이내믹하게 생성하는 작업장 관리 애플리케이션을 개발하였습니다. 이렇게 생성된 프로세스는 BPEL로 다이내믹하게 변환된 후 Oracle BPEL Server에 deploy됩니다. 비즈니스 사용자에 대한 프로세스 의존도가 높은 기업 환경이라면 이와 같은 접근 방식을 심각하게 고려할 필요가 있습니다. 커스텀 디자이너 기반의 접근 방법을 통해 사용자로부터 BPEL의 복잡성을 격리할 수 있을 뿐 아니라, 어댑티브 BPEL 프로세스를 다이내믹하게 생성하기 위한 독자적인 플랫폼을 구현하는 것이 가능합니다. 이와 같은 방법으로 SOA의 혜택을 신속하게 실현하고 구체화할 수 있을 것입니다.


Jerry ThomasJerry Thomas [jthomas@centerstonesoft.com] 는 Centerstone Software의 수석 설계담당자로, 세계적인 대규모 기업의 부동산, 시설, 인력, 자산, 리스, 작업장 관리를 자동화하는 솔루션을 개발하고 있습니다. 토마스는 CenterStone의 엔터프라이즈 작업장 관리 제품 및 웹 서비스, BPEL, 시스템 인프라스트럭처 등을 주로 담당하고 있습니다. CenterStone에 근무하기 전까지, 그는 Riverton, ONTOS, Hewlett-Packard 등의 업체에서 컨설턴트, 수석 개발자 등의 직책을 역임하였습니다.

여러분의 의견을 보내 주십시오
E-mail this page
Printer View Printer View