SOA 베스트 프랙티스: BPEL Cookbook

WSIF를 이용한 통합
Matjaz B. Juric

BPEL 프로세스가 WSIF를 이용해서 Java 클래스 및 EJB에 액세스하도록 구현하는 방법에 대해 알아봅니다.

전체 BPEL Cookbook 목록 보기

아티클 관련 다운로드:
샘플 코드
Oracle BPEL Process Manager 및 Designer
Oracle JDeveloper 10.1.3 또는 이후 버전

작성일 2005년 10월

실제 환경에서 BPEL 비즈니스 프로세스를 기존 애플리케이션 또는 시스템에 연결해야 할 경우가 종종 발생하곤 합니다. 이 문서는 Java 클래스, EJB(Enterprise Java Beans), JMS(Java Message Service), ERP 시스템 등의 J2EE 구성요소를 JCA(Java Connector Architecture), JDBC 데이터베이스 및 기타 Java 리소스를 통해 연결하는 방법에 초점을 맞추고 있습니다.
물론 이러한 리소스를 웹 서비스로 변환할 수도 있습니다. 하지만 이 방법에는 몇 가지 문제가 있습니다:

  • 네이티브 Java 클래스를 호출하는 경우, 또는 EJB 및 기타 네이티브 Java 리소스를 호출하는 경우에 비해 웹 서비스의 호출 작업에 훨씬 높은 성능 오버헤드가 수반됩니다 (오른쪽 글상자 참고).
  • 웹 서비스 호출 과정에서, 트랜잭션 컨텍스트(transaction context)를 전달하는 중요한 기능을 사용할 수 없습니다. 이에 반해, EJB, JCA 등의 Java 리소스를 직접 사용하는 경우에는 트랜잭션 컨텍스트의 자동 전달이 가능합니다 (단, Java 리소스가 이러한 기능을 지원하는 경우에 한정).
이러한 이유 때문에라도, 이러한 외부 리소스를 네이티브한 방법으로 액세스하는 방법이 보다 바람직합니다. Java 리소스에 대한 네이티브 연결은 BPEL의 표준 기능은 아닙니다. 하지만 Oracle BPEL Process Manager가 이를 위한 솔루션으로 WSIF(Web Services Invocation Framework)를 제공하고 있습니다. WSIF를 이용하는 경우 BPEL 코드의 변경 또는 확장이 불필요합니다. 이러한 기능을 활용하여 BPEL의 활용 범위를 더욱 확장하고 EAI(enterprise application integration)을 위한 새로운 대안을 구현하는 것이 가능합니다.

BPEL Cookbook시리즈의 이번 연재에서는, 웹 서비스가 아닌 다른 리소스에 대한 액세스를 위해 BPEL 프로세스에서 WSIF를 활용하는 방법에 대해 알아보기로 합니다.

WSIF의 이해
SOAP와 WSIF Binding의 성능 및 트랜잭션 차이 비교

보통 Java 리소스를 호출하는 것이 웹 서비스를 호출하는 경우에 비해 빠릅니다. 특히 플레인(plain) Java 클래스를 사용하는 경우, 메소드 호출의 성능은 웹 서비스에 비해 몇 십 배 이상 빠른 성능을 보이기도 합니다. 이는 애플리케이션 서버 프로세스에 로드된 Java 클래스의 메소드를 BPEL 프로세스가 직접 액세스하기 때문입니다.

EJB를 사용하는 경우에는 로컬 인터페이스(local interface) 또는 원격 인터페이스(remote interface) 중 하나를 선택할 수 있습니다. 로컬 인터페이스는 플레인 Java 클래스에 거의 근접하는 성능을 보입니다. 다만 EJB 컨테이너에 수반되는 오버헤드로 인해 약간의 성능 차가 발생할 수 있습니다. 하지만 원격 EJB 인터페이스를 사용하는 경우에는 성능 오버헤드의 차이가 좀 더 크게 벌어 질 수 있습니다 (하지만 웹 서비스와 비교한다면 여전히 빠른 성능을 보여 줍니다).

EJB 원격 인터페이스는 RMI-IIOP를 통해 액세스됩니다. RMI-IIOP는 클라이언트 사이드 모듈과 서버 사이드 스켈리튼(skeleton)을 필요로 합니다. 원격 메소드 호출이 서버 사이드의 스켈리튼에 전달되려면 여러 계층을 거쳐야 하며, 여기에는 많은 시간이 소요됩니다. 따라서 원격 인터페이스를 이용하는 경우에는 대단위(coarse-grained) 방법론을 사용하는 한편, 성능에 영향을 미칠 수 있는 다른 J2EE 패턴을 함께 고려할 필요할 필요가 있습니다. 또 일부 애플리케이션 서버는, 동일한 애플리케이션 서버에 EJB가 deploy된 경우에만 EJB 커뮤니케이션의 최적화 기능을 제공한다는 점을 주의해야 합니다.

웹 서비스의 성능 문제는 좀 더 복잡합니다. 일반적으로 웹 서비스의 커뮤니케이션 성능은 EJB 원격 인터페이스의 성능과 비슷한 수준을 갖습니다. IIOP를 사용하는 EJB와 달리, 웹 서비스는 SOAP를 사용합니다. SOAP는 바이너리 IIOP에 비해 효율성이 떨어집니다. 따라서 SOAP 메시지의 작성 및 파싱, XML serialization 등에 관련하여 호출자와 수신자 모두에 프로세싱 오버헤드가 수반됩니다. 필자의 테스트 결과에 따르면, 웹 서비스의 호출 작업은 세션 빈(session bean)의 호출에 비해 “5배에서 9배” 정도 느린 것으로 확인되었습니다.
WSIF를 이용한 Java 리소스의 호출 방법은 트랜잭션 관리 측면에서도 장점을 제공합니다. EJB, JCA와 같은 Java 리소스는 JTA와 JTS를 통해 트랜잭션을 지원합니다. JTS는 X/Open DTP 표준 기반의 분산 트랜잭션을 지원하는 CORBA Object Transaction Service를 기반으로 하는 테크놀로지입니다. JTA를 통해 트랜잭션을 지원하는 Java 리소스(EJB, JCA 등)는 2PC(Two-Phase Commit) 프로토콜을 이용하는 분산 트랜잭션에 참여할 수 있습니다.

WSIF는 XA 인터페이스를 이용하여 Java 리소스 간의 트랜잭션 컨텍스트(transactional context) 정보를 자동 전달(propagate)합니다. 따라서 WSIF를 이용하여 BPEL 프로세스에서 다수의 “transaction-aware” 리소스(예: 두 개의 EJB)를 사용하는 경우, 트랜잭션 컨텍스트는 모든 리소스에 자동으로 전달니다. 또 예외(exception)가 발생하면 전체 트랜잭션이 자동으로 롤백 되므로, BPEL 프로세스에 별도의 예외 처리 메커니즘을 정의할 필요가 없습니다. WSIF를 지원하는 Java 리소스를 사용하지 않는 경우에는 이러한 기능적 장점을 활용할 수가 없습니다. 이런 경우라면, 각각의 웹 서비스마다 별도의 예외 처리 메커니즘을 수동으로 구현해 주어야 할 것입니다. WSIF는 미션 크리티컬 프로세스의 설계 및 개발에 있어 매우 중요하게 활용되는 테크놀로지입니다.

서적 구매에 관련한 비즈니스 프로세스를 예로 들어 보겠습니다. 구현된 비동기식 프로세스(asynchronous process)는 3 개의 웹 서비스를 갖습니다: Book Rating 웹 서비스는 특정 서적의 평점을 0에서 5 사이의 숫자로 반환합니다. Book Store 웹 서비스는 서적의 가격을 반환하는 동일한 두 개의 서비스로 구성되어 있습니다. 프로세스는 두 서비스가 반환한 가격 중 낮은 쪽을 선택하고 서적을 구매합니다. 이 프로세스를 위한 오류 처리기(fault handler)가 구현되어 있으며, 프로세스는 여러 개의 스코프(scope)로 구분됩니다 (1_BuyBook\BuyBook.bpel 파일 참고).

서적의 평점을 얻기 위해 EIS(enterprise information system)가 제공하는 Java 클래스, EJB (세션 빈), 서비스를 JCA 또는 이와 유사한 Java 리소스를 통해 액세스한다고 가정해 봅시다. Oracle BPEL Process Manager를 이용해서 이러한 리소스(또는 바인딩이 필요한 다른 리소스)를 BPEL 프로세스에 포함시키려면, 서비스 바인딩(WSDL)만을 수정하면 됩니다 (BPEL 프로세스 자체는 수정할 필요가 없습니다). Book Rating Web 서비스를 Java 클래스로 대체하고자 한다면, 웹 서비스의 WSDL만을 변경하면 됩니다 (뒷부분에서 자세하게 설명합니다).

이러한 접근방식을 가능하게 하기 위한 기반 기술로 WSIF가 활용됩니다. WSIF는 본래 IBM alphaWorks의 Web Service Toolkit에 포함시킬 목적으로 개발된 Apache 테크놀로지입니다. WSIF는 SOAP를 기반으로 하는 웹 서비스가 아닌 서비스에 대해서도, WSDL의 서비스 기술(service description)을 이용하여 웹 서비스 모델을 확장할 수 있게 해 줍니다. 또 WSIF는 이러한 서비스를 실제 임플리먼테이션(implementation) 및 프로토콜(protocol)로 매핑하는 것을 가능하게 합니다. 다시 말해 BPEL 프로세스가 사용하는 파트너 웹 서비스가 어떤 것이든, 웹 서비스의 요약 기술(abstract description)을 관련 리소스에 바인딩하고, WSIF 바인딩을 이용하여 커뮤니케이션을 수행하는 것이 가능합니다. Oracle BPEL Process Manager 10.1.2에 사용되는 WSIF는 Java 클래스, EJB, JCA, HTTP GET/POST, 소켓 등을 지원합니다. 또 커스텀 WSIF 바인딩을 정의하여 어떤 리소스든지 BPEL에서 사용하도록 구성할 수 있습니다.

이러한 기능을 이용하면 B2B 환경뿐 아니라 EAI 환경에서도 BPEL을 효과적으로 활용할 수 있습니다. EIS(enterprise information system)은 일반적으로 여러 가지 소프트웨어 컴포넌트를 포함하고 있으며, 이기종 플랫폼 환경의 JCA, EJB, 웹 서비스 등을 통해 레거시 애플리케이션에 액세스하는 복잡한 구조를 갖습니다. 이처럼 다양한 소프트웨어 컴포넌트들을 통합하기 위해서는 여러 가지 프로토콜이 사용되어야 합니다. 예를 들어, 소프트웨어가 다른 서버로 마이그레이션 되거나 업그레이드된 경우, 통합 코드 또한 함께 업그레이드해야 할 수 있습니다. WSIF를 사용한다면 이러한 번거로움을 피할 수 있습니다.

그 밖에 WSIF가 제공하는 중요한 혜택이 다음과 같습니다:
  • SIF를 통해 서비스를 호출함으로써, 네이티브 프로토콜의 성능 수준을 유지할 수 있습니다. 따라서 Java 리소스, 네이티브 Java 클래스, EJB, 또는 다른 리소스를 호출하는 경우에도 웹 서비스에 수반되는 성능 오버헤드의 문제를 피할 수 있습니다.
  • SIF는 Java Transaction API(JTA)를 이용하여 “transaction-aware” Java 리소스의 호출 과정에서 트랜잭션 컨텍스트(transaction context)를 자동으로 전달(propagate)합니다.
WSIF의 동작 원리를 이해하기 위해, 서적 구매를 위한 BPEL 프로세스를 수정한 후 Java 클래스와 EJB를 호출해 보기로 하겠습니다. WSIF를 사용하는 경우에는 BPEL 코드의 변경이 불필요하며, 서비스의 WSDL만을 수정하는 것으로 충분하다는 사실을 다시 한 번 상기하시기 바랍니다. 이처럼 WSDL을 수정함으로써 (웹 서비스를 바인딩하는 것이 아니라) Java 리소스에 대한 호출을 바인딩하게 됩니다.

먼저, Book Rating 웹 서비스를 대체할 Java 클래스를 구현하는 부분에 초점을 맞추어 보겠습니다. 웹 서비스를Java 클래스로 대체하려면, 웹 서비스와 똑같은 인터페이스를 갖는 Java 클래스가 구현되어 있어야 하며, 따라서 WSDL의 요구사항에 기반한 Java 클래스의 개발 작업을 수행해야 합니다. 또는 기존 Java 클래스 (또는 EJB와 같은 다른 리소스)에 맞도록 WSDL을 변경하는 방법도 생각해 볼 수 있습니다. 하지만 첫 번째 방법이 바람직합니다. 첫 번째 방법, 다시 말해 BPEL 프로세스의 요구사항에 맞도록 서비스 인터페이스를 변경하는 방법을 “contract-first” 접근법이라 부르기도 합니다

Java-to-XML 바인딩

Java 리소스를 BPEL에서 호출하는 과정에서는 BPEL 변수에 저장된 데이터가 이용됩니다. BPEL 변수의 값은 입력 매개변수의 형태로 Java 리소스에 전달됩니다. BPEL 변수는 XML로 구성되지만 Java 변수의 경우는 그렇지 않습니다. 따라서 XML과 Java 간의 매핑 작업이 필요합니다.
Java 환경에서 XML 데이터를 처리하는 방법에는 여러 가지가 있습니다:
  • DOM (Document Object Model) API를 이용하여 XML을 수작업으로 처리합니다. Java 메소드의 입력/출력 매개변수는 W3C DOM API가 제공하는 타입의 변수로 처리되며, DOM 메소드를 이용하여 XML을 직접 조작할 수 있습니다.

  • Java-XML 바인딩을 자동으로 수행합니다. Java-to-XML 바인딩을 통해 XML Schema 타입을 Java 타입으로 자동 변환할 수 있습니다. 이를 위해, XML 조작에 사용될 인터페이스와 Java 클래스를 생성해야 합니다. 여기서 두 가지 대안이 가능합니다:
    • Oracle BPEL Process Manager는 XML façade를 이용한 Java-to-XML 바인딩을 기본으로 지원합니다.
    • 커스텀 Java 시리얼라이저(custom Java serializer)를 사용할 수 있습니다. 오라클은 JAXB (Java API for XML Bindings), XML Beans, Axis Beans 등을 지원하는 커스텀 시리얼라이저를 제공합니다. 또 개발자가 직접 시리얼라이저를 작성할 수도 있습니다 (뒷부분에서 자세히 설명합니다).
먼저 XML façade를 이용하는 방법에 대해 살펴보기로 하겠습니다.

XML Façade. XML façade는 Oracle BPEL Process Manage가 기본으로 지원하는 WSIF 기반 Java-to-XML 바인딩 테크놀로지입니다. XML façade에 포함된 Java 인터페이스와 클래스가 제공하는 get/set 메소드를 이용하면 BPEL 변수에 저장된 XML 데이터를 비교적 쉽게 액세스하고 수정할 수 있습니다. XML façade를 이용하면 XML을 직접 조작할 필요가 없으며, XML의 존재가 façade의 하부에 숨겨지므로 표준 Java 인터페이스를 이용한 데이터 조작이 가능해집니다 (이 개념을 XML serialization이라 부릅니다). XML façade는 빌트인 타입의 매핑을 통해 심플 데이터 타입(simple data type)을 지원하고, 컴플렉스 데이터 타입(complex data type)을 갖는 XML Schema로부터 Java 클래스를 생성할 수 있게 하는 것을 기본적인 목적으로 합니다.

XML Schema와 Java 타입 간의 기본적인 데이터 타입 매핑이 아래와 같습니다:

XML Schema 타입 Java 타입
·xs:string · java.lang.String
· char
· java.lang.Character
xs:int, xs:integer
· int
· java.lang.Integer
· java.math.BigInteger
xs:long · long
· java.lang.Long
xs:short
· short
· java.lang.Short
xs:float
· float
· java.lang.Float
xs:double
· double
· java.lang.Double
· java.math.BigDecimal
xs:byte
· byte
· java.lang.Byte
xs:Boolean
· boolean
· java.lang.Boolean
dateTime java.util.Calendar
date java.util.Date

위에서 확인할 수 있는 것처럼, 대부분의 심플 타입(simple type)은 프리미티브 타입(primitive type) 또는 오브젝트 타입(object type)으로 매핑이 가능합니다. 이것은 Java 코드에 사용된 실제 타입을 직접 매핑할 수 있다는 점에서 매우 유용한 기능입니다. 이와 별도로, WSDL의 <types> 섹션 또는 외부 XML Schema(XSD) 파일에 정의된 컴플렉스 타입(complex type)을 매핑할 수 있는 방법이 필요합니다. 예를 들어, Book Rating 웹 서비스 WSDL에는 BookDscType 타입의 BookRatingRequestMessage를 입력으로 취하는 작업이 포함되어 있습니다. BookDscType 컴플렉스 XML 타입은 BookRatingRequestMessage와 BookRatingRequest BPEL 변수를 위해 사용됩니다:

<xs:schema elementFormDefault="qualified" 
            targetNamespace="http://oracle.com/service/bookrating/">

   <xs:complexType name="BookDscType">
     <xs:sequence>
       <xs:element name="Title" type="xs:string" /> 
       <xs:element name="ISSN" type="xs:string" /> 
       <xs:element name="Publisher" type="xs:string" /> 
       <xs:element name="Authors" type="xs:string" /> 
     </xs:sequence>
   </xs:complexType>

</xs:schema>
XML façade는 BookDscType 컴플렉스 XML 타입을 위한 인터페이스와 클래스를 제공합니다. 따라서 Java getter 메소드를 이용하면 title, ISSN, publisher, author 등의 엘리먼트에 액세스할 수 있습니다. 또 XML façade가 제공하는 setter 메소드를 이용하면 엘리먼트를 수정할 수 있습니다.

BookDscType 컴플렉스 XML 타입을 위한 XML façade는 IBookDscType 인터페이스와 BookDscType 클래스로 구성되며 다음과 같은 메소드를 제공합니다:
  • getTitle() and setTitle()
  • getISSN() and setISSN()
  • getPublisher() and setPublisher()
  • getAuthors() and setAuthors()
또 BookDscTypeFactory 팩토리 클래스(factory class)의 createFacade() 메소드를 이용하면 IBookDscType를 생성할 수 있습니다. XML façade를 이용하면 코드가 더 단순해질 뿐 아니라 코드의 관리도 쉬워집니다. 특히 많은 수의 멤버 필드(member field)를 갖는 변수의 경우에는 더욱 그러합니다.

Oracle BPEL Process Manager는 schemac이라는 스키마 컴파일러 유틸리티를 제공합니다. 이 유틸리티를 이용하면 XML façade를 생성할 수 있습니다. BookRating.wsdl을 위한 XML façade를 생성하려면 커맨드 라인에서 다음과 같이 실행합니다:
Z:\WSIF\2_JavaBindingClass>schemac BookRating.wsdl
----------------------------------------------------
Oracle XML Schema Processor Version 10.1.2.0.0
http://otn.oracle.com/bpel
Copyright (c) 2002-2004 - Oracle
(type schemac -help for help)
----------------------------------------------------

schemac> parsing schema file 'BookRating.wsdl' ...
schemac> Loaded schemas from wsdl located at BookRating.wsdl
schemac> generating XML business document ...
schemac> compiling XML business documents ...
Schemac completed successfully.

Z:\WSIF\2_JavaBindingClass>
생성된 XML façade의 클래스를 Java 리소스에서 사용하려면, 먼저 클래스를 컴파일하고 아래 디렉토리에 저장하여 BPEL 서버가 접근할 수 있도록 해야 합니다.

C:\OraBPELPM_1\integration\orabpel\system\classes

schemac 유틸리티는 몇 가지 옵션을 제공합니다. -d 스위치를 이용하면 생성된 façade 클래스들을 저장할 디렉토리 위치를 지정할 수 있습니다. façade 소스 코드를 보려면 -trace 옵션을 사용합니다. 또 Java 클래스로부터 XML Schema를 생성하는 용도로 schemac 유틸리티를 활용할 수도 있습니다. 이 기능은 서비스 인터페이스를 기존의 Java 리소스에 맞게 수정하고자 하는 경우에 유용합니다. 이 기능을 사용하려면 -R 스위치 뒤에 (확장자를 제외한) Java 클래스 네임을 붙이면 됩니다

Java 클래스의 개발. BPEL 프로세스를 수정하지 않고 Book Rating 웹 서비스를 Java 클래스로 대체하려면, 기존의 Book Rating 웹 서비스와 동일한 인터페이스(또는 contract)를 갖는 Java 클래스를 생성해야 합니다. 이는 Java 클래스가 웹 서비스와 동일한 기능을 갖는 오퍼레이션(operation)을 제공해야 하며, 동일한 매개변수를 수신하고 동일한 결과 타입을 반환해야 함을 의미합니다. 단, 오퍼레이션 네임(operation name)은 동일하지 않아도 상관없습니다.

샘플 WSDL 파일에서 Book Rating 웹 서비스가 BookRating이라는 이름의 오퍼레이션을 제공하고 있음을 확인할 수 있을 것입니다. BookRating 오퍼레이션은 입력/출력 메시지를 모두 필요로 하는 동기식 작업을 수행합니다:

<portType name="BookRatingPT">
  <operation name="BookRating">
    <input message="tns:BookRatingRequestMessage" /> 
    <output message="tns:BookRatingResponseMessage" /> 
  </operation>
</portType>

The signatures of both messages are as follows:
<message name="BookRatingRequestMessage">
  <part name="book" type="tns:BookDscType" /> 
</message>

<message name="BookRatingResponseMessage">
  <part name="rating" type="xs:int" />
</message>
BookRating 오퍼레이션은 BookDscType 타입의 입력 매개변수를 갖습니다. BookDscType을 Java로 매핑하려면, 먼저 schemac 툴을 사용하여 XML façade를 생성해 두어야 합니다. 오퍼레이션이 반환하는 BookRatingResponseMessage 메시지는 xs:int 타입을 갖습니다. xs:int 타입은 java.lang.Integer로 매핑됩니다. (int 또는 java.math.BigInteger로의 매핑도 가능하지만, 여기에서는 java.lang.Integer를 사용하는 것으로 합니다.)

이제 Book Rating 웹 서비스를 대체할 Java 클래스를 작성할 준비가 완료되었습니다. 새로운 Java 클래스는 BookRatingJava라는 이름을 가지며, getBookRating이라는 하나의 메소드를 갖습니다. 메소드의 코드는 아주 단순합니다. 서버 콘솔에 메시지를 디스플레이하고 4의 rating 값을 반환합니다. (물론 실제 환경이라면 데이터베이스 쿼리를 통해 rating 값을 얻어야 할 것입니다.) 코드는 아래와 같습니다. getTitle() 메소드와 getISSN() 메소드를 이용하여 book title과 ISSN 정보에 액세스하고 있음을 주목하시기 바랍니다.

import com.oracle.service.bookrating.*;

public class BookRatingJava {

  public Integer getBookRating (BookDscType book) {

    System.out.println("Book rating for "+book.getTitle()+" ("+book.getISSN()+"): 4.");

    return new Integer(4);

  }
}
콘솔 출력 결과를 통해 프로세스가 웹 서비스가 아닌 Java 클래스를 호출하고 있음을 확인할 수 있습니다.

WSDL에 WSIF 바인딩을 정의. BPEL 프로세스가 웹 서비스 대신 Java 클래스를 사용하도록 하려면, Java 클래스에 대한 WSIF 바인딩을 설정해 주어야 합니다. 이를 위해 Book Rating WSDL에 binding 섹션을 추가해야 합니다.

각각의 WSIF 바인딩은 두 개의 영역으로 나뉘어 집니다. 첫 번째로, 실제 바인딩을 정의하는 영역에서는 다음과 같은 사항이 명시됩니다:
  • 사용되는 바인딩의 종류 (Java 클래스, EJB, JCA 등)
  • 타입의 매핑 – XML 타입을 대상 타입(Java 리소스의 경우 Java 타입)으로 매핑하는 방법을 지정합니다 컴플렉스 타입의 경우에는 매핑을 일일이 정의해 주어야 합니다. 심플 타입은 자동으로 위의 표에 지정된 방식대로 자동 매핑됩니다.
  • 오퍼레이션의 매핑 – (<portType> 태그에 정의된) 각 WSDL 오퍼레이션을 대상 리소스(예: Java 클래스의 메소드 네임)로 매핑하는 방법을 지정합니다.
두 번째 영역에서는 사용할 서비스를 명시합니다. 여기서 리소스의 정확한 이름을 정의하게 됩니다. Java 클래스가 사용되는 경우에는 (패키지 네임을 포함하는) 전체 클래스 네임이 명시되어야 합니다.

실제 환경에서는 리소스(Java 클래스, EJB 등)를 위한 WSDL이 존재하지 않을 수도 있습니다. 이런 경우라면 다음과 같은 과정을 거쳐야 합니다:
  1. Java-to-XML 바인딩을 생성하고, 입력 매개변수와 리턴 값을 XML으로 매핑하는 방법을 정의합니다. 이때 XML façades와 schemac 툴의 –R 스위치를 이용하면 Java 클래스를 기반으로 하는 XML 스키마를 쉽게 생성할 수 있습니다.
  2. 각 오퍼레이션의 시그니처와 입력/출력 메시지를 정의합니다. (에러 핸들링에 대해서는 뒷부분에서 설명합니다.)
  3. WSIF 바인딩을 추가합니다.
  4. <partnerLinkType> 선언을 추가하여, BPEL 프로세스로부터 WSDL을 사용할 수 있게 합니다.
특히 처음의 두 단계에서는 툴 또는 마법사를 이용하여 리소스를 웹 서비스로 자동 변환할 수 있습니다. 이때 실제로 리소스가 웹 서비스로 변환되는 것은 아니며, 대신 새로 생성된 WSDL을 사용하게 됩니다.

Java 클래스를 위한 WSIF 바인딩. 이제 Book Rating Java 클래스를 위한 WSIF 바인딩을 정의해 봅시다. WSIF 프로바이더에 의해 사용되는 두 개의 네임스페이스(namespace)를 WSDL 다큐먼트 루트 엘리먼트의 <definitions> 태그에 정의합니다. 타입 매핑(type mapping)을 위해서는 포맷 네임스페이스(format namespace)가, 오퍼레이션 매핑(operation mapping)을 위해서는 java 네임스페이스가 사용되며, Java 클래스의 풀 네임이 사용됩니다:
<?xml version="1.0" encoding="utf-8" ?> 
<definitions xmlns:xs="http://www.w3.org/2001/XMLSchema" 
             xmlns:tns="http://oracle.com/service/bookrating/" 
             targetNamespace="http://oracle.com/service/bookrating/" 
             xmlns="http://schemas.xmlsoap.org/wsdl/"
             xmlns:plnk="http://schemas.xmlsoap.org/ws/2003/05/partner-link/" 
             xmlns:format="http://schemas.xmlsoap.org/wsdl/formatbinding/"
             xmlns:java="http://schemas.xmlsoap.org/wsdl/java/" >
...
다음으로, binding 섹션을 추가합니다. 이 섹션은 일반적으로 port type 선언의 뒷부분, 그리고 partner link type의 앞부분에 위치합니다. 여기서 BookRatingPT 포트 타입을 위한 Java 바인딩을 정의합니다:
  1. XML에서 Java로의 타입 매핑을 정의합니다. 입력 매개변수의 BookDscType XML 타입은 com.oracle.service.bookrating.BookDscType Java 클래스로 매핑됩니다. 출력 매개변수의 xs:int 타입은 매핑을 따로 정의할 필요가 없으며, 자동으로 java.lang.Integer으로 매핑됩니다.
  2. WSDL 오퍼레이션 BookRating이 Java 메소드 getBookRating()으로 매핑됨을 정의합니다. WSDL 오퍼레이션 네임과 Java 클래스의 메소드 네임은 같을 필요가 없지만, 입력/반환 매개변수의 타입은 동일하게 설정되어야 합니다:
    ...
    <binding name="JavaBinding" type="tns:BookRatingPT">
    
        <java:binding/>
    
        <format:typeMapping encoding="Java" style="Java">
    
          <format:typeMap typeName="tns:BookDscType" 
                  formatType="com.oracle.service.bookrating.BookDscType" />
    
        </format:typeMapping>
    
        <operation name="BookRating">
    
          <java:operation methodName="getBookRating"/>
    
          <input/>
          <output/>
        </operation>     
    
      </binding>
    ...
    
다음에는, 사용되는 서비스를 정의합니다. 여기서는 서비스가 Java 클래스를 통해 제공됨을 명시합니다. Book Rating 서비스는 com.oracle.rating.BookRatingJava Java 클래스를 사용합니다:
...
  <service name="BookRating">

    <port name="JavaPort" binding="tns:JavaBinding">

      <java:address className="com.oracle.rating.BookRatingJava"/>

    </port>

  </service>
Book Rating WSDL의 나머지 부분(partner link type 포함)은 변경되지 않습니다.

샘플 코드의 테스트. 이제 샘플 코드를 테스트하고 BPEL 프로세스가 기존의 웹 서비스 대신 Java 클래스를 사용하는지 확인할 준비가 거의 완료되었습니다. 지금까지 WSDL만을 변경하였으며, BPEL 프로세스 코드에는 아무런 수정도 가하지 않았음을 상기하시기 바랍니다.
이제 기존의 BPEL 코드와 파트너 링크, 그리고 웹 서비스 호출에 사용했던 <invoke> 액티비티를 그대로 사용하여 BookRatingJava Java 클래스를 호출하게 됩니다. Book Rating 서비스의 호출을 위한 BPEL 코드가 아래와 같습니다:
...
          <!-- Synchronously invoke the Book Rating Web Service -->
          <scope name="BookRatingInvoke">
          
            <faultHandlers>

              <catchAll>
                <!-- If book rating is not available assign 0 -->
                <assign>
                  <copy>
                    <from expression="number(0)"/>
                    <to variable="BookRatingResponse" part="rating"/>
                  </copy>
                </assign>          
              </catchAll>
              
            </faultHandlers>
            
            <invoke partnerLink="BookRating" 
                  portType="bkr:BookRatingPT" 
                  operation="BookRating"
                  inputVariable="BookRatingRequest" 
                  outputVariable="BookRatingResponse" />
          
          </scope>
...
샘플 코드를 테스트하기 전에, 몇 가지 “손질”이 필요합니다. 먼저, BPEL 프로세스가 수정된 WSDL을 사용하도록 설정해 주어야 합니다. 이를 위해, bpel.xml 파일을 수정하고, BookRating.wsdl 파일을 웹 서비스로부터 가져오는 대신 현재 디렉토리에서 가져오도록 수정합니다:
<?xml version="1.0" encoding="UTF-8"?>
<BPELSuitcase>
  <BPELProcess src="BuyBook.bpel" id="BuyBookJavaBinding">
    <partnerLinkBindings>
      <partnerLinkBinding name="Client">
        <property name="wsdlLocation">
           BuyBook.wsdl
        </property>
      </partnerLinkBinding>
      <partnerLinkBinding name="BookRating">
        <property name="wsdlLocation">
            BookRating.wsdl
        </property>
      </partnerLinkBinding>
      <partnerLinkBinding name="BookStore1">
        <property name="wsdlLocation">
            http://localhost:9700/orabpel/default/BookStore1/BookStore1?wsdl</property>
      </partnerLinkBinding>
      <partnerLinkBinding name="BookStore2">
        <property name="wsdlLocation">
            http://localhost:9700/orabpel/default/BookStore2/BookStore2?wsdl</property>
      </partnerLinkBinding>
    </partnerLinkBindings>
  </BPELProcess>
</BPELSuitcase>
다음으로 schemac 유틸리티를 이용하여 XML façade를 생성하고, BookRatingJava 클래스를 컴파일합니다. XML façade와 Java 클래스는 C:\OraBPELPM_1\integration\bpelpm\orabpel\system\classes 디렉토리에 deploy하여 BPEL 서버가 사용할 수 있게끔 합니다. 가장 쉬운 방법은, build.xml을 수정하여 scemac 컴파일러, javac 컴파일러, bpelc 컴파일러를 호출하도록 하는 것입니다:
<?xml version="1.0"?>
<project name="BuyBookJavaBinding" default="all" basedir=".">
    <property name="deploy" value="default"/>
    <property name="rev" value="1.0"/>

    <target name="CompileJava">
        <schemac input="${basedir}/BookRating.wsdl" out="${home}/system/classes"/>
        <javac srcdir="${basedir}/src" destdir="${home}/system/classes"/>
    </target> 
       
    <target name="main">
        <bpelc home="${home}" rev="${rev}" deploy="${deploy}"/>        
    </target>    

    <target name="all" depends="CompileJava, main"/>

</project>
A이제 obant 유틸리티를 실행하면 다음과 같은 출력을 얻게 됩니다:
Z:\WSIF\2_JavaBindingClass>obant

Z:\WSIF\2_JavaBindingClass>SETLOCAL
Buildfile: build.xml

CompileJava:
  [schemac] schemac> parsing schema file 'Z:\WSIF\2_JavaBindingClass/BookRating.
wsdl' ...
  [schemac] schemac> Loaded schemas from wsdl located at Z:\WSIF\2_JavaBindingCl
ass/BookRating.wsdl
  [schemac] schemac> generating XML business document ...
  [schemac] schemac> compiling XML business documents ...
    [javac] Compiling 1 source file to C:\OraBPELPM_1\integration\orabpel\system
\classes

main:
    [bpelc] validating "Z:\WSIF\2_JavaBindingClass\BuyBook.bpel" ...
    [bpelc] BPEL suitcase deployed to: C:\OraBPELPM_1\integration\orabpel\domain
s\default\deploy

all:

BUILD SUCCESSFUL
Total time: 17 seconds

Z:\WSIF\2_JavaBindingClass>ENDLOCAL

Z:\WSIF\2_JavaBindingClass>
다음으로, BPEL Console에서 프로세스를 실행합니다. Visual Flow 윈도우에서 프로세스의 실행 상태를 모니터링합니다. <invoke> 액티비티가 사용되고, book rating이 4로 설정되었음을 확인할 수 있습니다 (기존 Book Rating 웹 서비스의 경우 5를 반환합니다):

figure 1

BPEL Process Manager가 Java 클래스를 호출했음을 분명하게 검증하려면, BPEL Process Manager 콘솔에서 아래의 출력 결과를 확인합니다:
05/10/19 19:35:36 Book rating for Business Process Execution Language (1-904811-
18-3): 4.
Exception Handling

BPEL에서 Java 리소스를 호출하는 과정에서, Java의 exception을 BPEL로 전달하기 위한 방법이 필요합니다. WSIF를 이용하면 Java exception을 WSDL fault로 매핑하고, BPEL fault handler를 이용하여 처리할 수 있습니다. 매핑 작업은 exception serializer에 의해 처리됩니다. Oracle BPEL Process Manager는 default exception serializer를 지원합니다. 또는 직접 커스텀 serializer를 작성할 수도 있습니다.

Java exception을 BPEL에 전달하는 방법의 예시를 위해 샘플 코드를 확장해 보기로 하겠습니다. 먼저 default serializer를 사용하고, 그런 다음 custom serializer를 이용하여 샘플 코드를 확장하기로 합니다. 아래와 같이 작업을 수행합니다:
  1. Java에 user exception을 정의합니다.
  2. BookRatingJava Java 클래스를 수정하여 exception을 발생시키도록 합니다.
  3. WSDL에 이에 대응하는 fault를 정의합니다. 이를 위해 fault 메시지의 XML Schema 타입, fault 메시지를 정의하고 <fault> 메시지를 WSDL <operation> description에 추가합니다.
  4. exception을 위한 WSIF 바인딩을 정의합니다.
Java에서 User Exception 정의. 먼저, 요청된 서적이 존재하지 않는 경우 발생시킬 user exception을 정의합니다. exception의 이름은 BookDoesNotExistException으로 설정합니다. Java exception을 위한 코드가 아래와 같습니다:
package com.oracle.rating;

public class BookDoesNotExistException extends Exception 
{
  String detailDesc;
  String bookTitle;

  public BookDoesNotExistException(String message, String detailDesc, String bookTitle)
  {
    super(message);
    this.detailDesc = detailDesc;
    this.bookTitle = bookTitle;
  }

  public String getDetailDesc()
  {
    return detailDesc;
  }

  public String getBookTitle()
  {
    return bookTitle;
  }
}
그런 다음, BookRatingJava Java 클래스를 수정합니다. 서적이 ISSN이 999인 경우 exception을 발생시키도록 설정합니다:
package com.oracle.rating;

import com.oracle.service.bookrating.*;

public class BookRatingJavaUserException {

  public Integer getBookRating (BookDscType book) throws BookDoesNotExistException {

    if (book.getISSN().equals("999"))
       throw(new BookDoesNotExistException("Book does not exist",
                                           "Book (ISSN="+book.getISSN()+") does not exist",
                                           book.getTitle()));

    System.out.println("Book rating for "+book.getTitle()+" ("+book.getISSN()+"): 4.");

    return new Integer(4);

  }
}
WSDL Fault의 정의. 다음 단계에서는, Java exception에 대응하는 fault를 WSDL에 정의합니다. default exception serializer를 사용하는 경우, 두 가지 엘리먼트(faultstring, detail)를 갖는 컴플렉스 타입을 사용하게 됩니다. 이 컴플렉스 타입을 Book Rating WSDL의 <types> 섹션에 추가합니다:
<xs:complexType name="BookDoesNotExistExceptionType">
  <xs:sequence>
    <xs:element name="faultstring" type="xs:string" /> 
    <xs:element name="detail" type="xs:string" /> 
  </xs:sequence>
</xs:complexType>
다음으로, fault 메시지를 정의합니다:
<message name="BookDoesNotExistException">
  <part name="exception" type="tns:BookDoesNotExistExceptionType" />
</message>
마지막으로fault 메시지를 BookRating 오퍼레이션 시그니처(operation signature)에 추가합니다:
<portType name="BookRatingPT">
  <operation name="BookRating">
    <input message="tns:BookRatingRequestMessage" /> 
    <output message="tns:BookRatingResponseMessage" /> 
    <fault name="BookDoesNotExistException" message="tns:BookDoesNotExistException" />
  </operation>
</portType>
default exception serializer가 fault 엘리먼트를 생성하고, Exception.getMessage()에 의해 반환된 컨텐트를 faultstring에, Exception.toString()에 의해 반환된 컨텐트를 detail 엘리먼트에 입력합니다.

Exception을 위한 WSIF 바인딩 정의. 이제 WSIF 바인딩에 exception을 추가할 준비가 완료되었습니다. 먼저 BookDoesNotExistExceptionType XML 타입을 위한 타입 매핑을 정의해야 합니다. (여기에서는 Java exception 클래스 com.oracle.rating.BookDoesNotExistException으로 매핑합니다.) 또 fault message name(BookDoesNotExistException)을 오퍼레이션 매핑에 추가해야 합니다:
<binding name="JavaBinding" type="tns:BookRatingPT">
  <java:binding/>
  <format:typeMapping encoding="Java" style="Java">
    <format:typeMap typeName="tns:BookDscType" 
                    formatType="com.oracle.service.bookrating.BookDscType" />
    <format:typeMap typeName="tns:BookDoesNotExistExceptionType" 
                    formatType="com.oracle.rating.BookDoesNotExistException" />
  </format:typeMapping>
  <operation name="BookRating">
    <java:operation methodName="getBookRating"/>
    <input/>
    <output/>
    <fault name="BookDoesNotExistException"/> 
  </operation>     
</binding>
샘플 코드가 정상적으로 실행되려면, Java 클래스를 컴파일해서 C:\OraBPELPM_1\integration\orabpel\system\classes 디렉토리에 저장하고 BPEL 서버가 액세스할 수 있게 해야 합니다. obant 유틸리티를 이용하면 이 작업을 간단하게 완료할 수 있습니다:
Z:\WSIF\3_JavaBindingUserExceptionDefaultSerializer>obant

Z:\WSIF\3_JavaBindingUserExceptionDefaultSerializer>SETLOCAL
Buildfile: build.xml

CompileJava:
  [schemac] schemac> parsing schema file 'Z:\WSIF\3_JavaBindingUserExceptionDefa
ultSerializer/BookRating.wsdl' ...
  [schemac] schemac> Loaded schemas from wsdl located at Z:\WSIF\3_JavaBindingUs
erExceptionDefaultSerializer/BookRating.wsdl
  [schemac] schemac> generating XML business document ...
  [schemac] schemac> compiling XML business documents ...
    [javac] Compiling 2 source files to C:\OraBPELPM_1\integration\orabpel\syste
m\classes

main:
    [bpelc] validating "Z:\WSIF\3_JavaBindingUserExceptionDefaultSerializer\BuyB
ook.bpel" ...
    [bpelc] BPEL suitcase deployed to: C:\OraBPELPM_1\integration\orabpel\domain
s\default\deploy

all:

BUILD SUCCESSFUL
Total time: 18 seconds

Z:\WSIF\3_JavaBindingUserExceptionDefaultSerializer>ENDLOCAL

Z:\WSIF\3_JavaBindingUserExceptionDefaultSerializer>
BPEL 콘솔에서 샘플 코드를 실행하고 ISSN으로 999를 입력하면 아래와 같은 결과를 얻습니다:

figure 2

Custom Exception Serializer. WSDL default exception serializer의 구조(faultstring과 detail 엘리먼트)로는 요구사항을 만족할 수 없는 경우, custom exception serializer를 사용할 수 있습니다. custom exception serializer는 exception과 그 속성을 WSDL fault에서 사용되는 컴플렉스 타입으로 매핑해 주는 Java 클래스입니다. custom serializer을 작성하는 방법이 아래와 같습니다:

  1. WSDL fault 메시지를 위한 커스텀 컴플렉스 타입(custom complex type)을 정의합니다
  2. customer exception serializer를 작성하여 Java exception을 WSDL fault로 전달합니다.
  3. custom exception serializer를 등록합니다.
먼저, Java exception을 위해 사용할 XML Schema 커스텀 컴플렉스 타입을 정의하고, exception의 세 가지 속성(message, detail, description)을 모두 포함시킵니다. 새로 정의한 컴플렉스 타입은 Book Rating WSDL의 <types> 섹션의 디폴트 컴플렉스 타입을 대체하게 됩니다:
<xs:complexType name="BookDoesNotExistExceptionType">
  <xs:sequence>
    <xs:element name="message" type="xs:string" /> 
    <xs:element name="detailDesc" type="xs:string" /> 
    <xs:element name="bookTitle" type="xs:string" /> 
  </xs:sequence>
</xs:complexType>
custom exception serializer는 Java exception을 WSDL fault 컴플렉스 타입에 매핑하는 방법을 정의하기 위한 Java 클래스입니다. Java exception의 속성을 fault 메시지의 XML 엘리먼트에 매핑하기 위해, exception serializer에 다음과 같은 인터페이스가 구현되어야 합니다:
public interface IExceptionSerializer {

	public Element serialize(Throwable ex, 
                             String messageName, 
                             String namespaceURI);
}
예제에서는, custom exception serializer의 이름을 BookDoesNotExistExceptionSerializer로 설정하고 기존의 ExceptionSerializer 클래스를 확장하도록 합니다. DOM API를 사용하여 Java exception의 세 가지 속성(message, detail description, book title)을 XML Schema 타입 BookDoesNotExistExceptionType으로 매핑하게 됩니다:
package com.oracle.rating;

import org.w3c.dom.Element;

import com.collaxa.xml.XMLHelper;
import com.oracle.bpel.xml.util.ExceptionSerializer;
import com.oracle.bpel.xml.util.IExceptionSerializer;

public class BookDoesNotExistExceptionSerializer extends ExceptionSerializer
		implements IExceptionSerializer {

	public Element serialize(Throwable ex, String messageName, String namespaceURI) {
		
		if(ex instanceof BookDoesNotExistException)
		{
			BookDoesNotExistException brEx = (BookDoesNotExistException)ex;
			
			Element exceptionElement = 
                         XMLHelper.createRootElement(messageName, namespaceURI,"tns");
			
			Element messageElement = 
                         XMLHelper.createElement("message","tns",namespaceURI);
			messageElement.setNodeValue(brEx.getMessage());
			exceptionElement.appendChild(messageElement);
			
			Element detailElement = 
                         XMLHelper.createElement("detailDesc","tns",namespaceURI);
			detailElement.setNodeValue(brEx.getDetailDesc());
			exceptionElement.appendChild(detailElement);
			
			Element bookElement = 
                          XMLHelper.createElement("bookTitle","tns",namespaceURI);
			bookElement.setNodeValue(brEx.getBookTitle());
			exceptionElement.appendChild(bookElement);		

			return exceptionElement;

		}
		return super.serialize(ex, messageName, namespaceURI);
	}

}
마지막으로 custom exception serializer를 Oracle BPEL Process Manager에 등록합니다. 이와 같이 함으로써, BPEL Process Manager가 default serializer 대신 custom serializer를 사용하게 됩니다. 이를 위해, bpel.xml deployment descriptor에서 exceptionSerializer property를 정의합니다:
<partnerLinkBinding name="BookRating">
  <property name="wsdlLocation">
      BookRating.wsdl
  </property>
  <property name="exceptionSerializer">
      com.oracle.rating.BookDoesNotExistExceptionSerializer
  </property>	        
</partnerLinkBinding>
위의 경우와 마찬가지로, Java 클래스를 컴파일하고 C:\OraBPELPM_1\integration\orabpel\system\classes 디렉토리에 저장합니다. 이를 위해 obant 유틸리티를 사용합니다:
Z:\WSIF\3_JavaBindingUserExceptionCustomSerializer>obant

Z:\WSIF\3_JavaBindingUserExceptionCustomSerializer>SETLOCAL
Buildfile: build.xml

CompileJava:
  [schemac] schemac> parsing schema file 'Z:\WSIF\3_JavaBindingUserExceptionCust
omSerializer/BookRating.wsdl' ...
  [schemac] schemac> Loaded schemas from wsdl located at Z:\WSIF\3_JavaBindingUs
erExceptionCustomSerializer/BookRating.wsdl
  [schemac] schemac> generating XML business document ...
  [schemac] schemac> compiling XML business documents ...
    [javac] Compiling 3 source files to C:\OraBPELPM_1\integration\orabpel\syste
m\classes

main:
    [bpelc] validating "Z:\WSIF\3_JavaBindingUserExceptionCustomSerializer\BuyBo
ok.bpel" ...
    [bpelc] BPEL suitcase deployed to: C:\OraBPELPM_1\integration\orabpel\domain
s\default\deploy

all:

BUILD SUCCESSFUL
Total time: 21 seconds

Z:\WSIF\3_JavaBindingUserExceptionCustomSerializer>ENDLOCAL

Z:\WSIF\3_JavaBindingUserExceptionCustomSerializer>
BPEL 콘솔에서 샘플 코드를 실행하고 ISSN 필드에 999를 입력하면 아래와 같은 결과를 얻게 됩니다. fault의 구조가 달라져 있음을 확인할 수 있습니다

figure 3

Custom Java Serializer. custom Java serializer는 IJavaSerializer 인터페이스의 구현을 위한 Java 클래스로, serialize와 deserialize의 두 가지 메소드를 제공합니다.

public interface IJavaSerializer {
	
	public Element serialize(Object obj, Class type, String name, String namespaceURI, 
                             String prefix, Map classMap) throws Exception;
	public Object deserialize(Element el, Class type) throws Exception;
}
custom exception serializer의 경우와 마찬가지로, 컴파일된 클래스를 C:\OraBPELPM_1\integration\orabpel\system\classes 디렉토리에 저장하고 bpel.xml deployment descriptor의 javaSerializer property를 정의합니다:
<partnerLinkBinding name="helper">
    <property name="wsdlLocation">HelperService.wsdl</property>
    <property name="javaSerializer">com.oracle.bpel.xml.util.MyCustomSerializer</property>
</partnerLinkBinding>
오라클은 JAXB(Java API for XML Binding), XML Beans, Axis Beans를 위한 세 가지 serializer를 기본 지원합니다:
Java Web Services Developer Pack에 포함된 JAXB는 개념적으로 XML façade와 유사하지만, 그 구현 방법에서 몇 가지 차이점을 갖습니다. Oracle BPEL Process Manager에서 JAXB serialization을 사용하려면 아래와 같이 작업합니다:
  1. JWSDP 설치 디렉토리(예: c:\jwsdp-1.5\jaxb\lib)를 C:\OraBPELPM_1\integration\orabpel\bin 디렉토리의 obsetenv.bat 파일과 C:\OraBPELPM_1\integration\orabpel\system\appserver\oc4j\j2ee\home\confi 디렉토리의 application.xml 파일의 BASE_OB_CLASSPATH 환경 변수에 추가합니다.
  2. JAXBSerializer.class 파일(샘플 코드 다운로드 참고)을 C:\OraBPELPM_1\integration\orabpel\system\classes\com\oracle\bpel\xml\util 디렉토리에 복사합니다.
  3. JAXB 컴파일러(xjc)가 생성한 Java 클래스를 C:\OraBPELPM_1\integration\orabpel\system\classes 디렉토리에 복사합니다.
또 BPEL 프로젝트에서 어떤 serializer를 사용할 것인지 정의해야 합니다. 이를 위해, bpel.xml 파일에서 javaSerializer property를 정의합니다:
<partnerLinkBinding name="helper">
  <property name="wsdlLocation">HelperService.wsdl</property>
  <property name="javaSerializer">com.oracle.bpel.xml.util.JAXBSerializer</property>
</partnerLinkBinding>
XML Bean은 Java-to-XML 바인딩을 위한 또 다른 대안을 제공합니다. XML Bean은 BEA에 의해 최초 개발된 이후 Apache 커뮤니티에 기증된 프로젝트입니다. XML façades 또는 JAXB와 달리, XML Bean은 Java 개발자로부터 XML을 완전히 숨기지 않습니다. 그 대신, getter/setter 메소드 및 “x” 접미어를 갖는 메소드들을 제공하는 Java 인터페이스를 통해, 필요한 경우 XML을 직접 조작할 수 있게 합니다. XML Bean은 또 XML Infoset에 대한 접근 기능을 제공합니다. XML이 Java 오브젝트로 변환되는 경우, 개발자는 전체 XML Infoset에 접근할 수 있습니다.

XML Bean을 사용하기 위한 설정 방법이 아래와 같습니다:
  1. XML Beans JAR 파일((BEA Weblogic의 xbean.jar 파일)을 C:\OraBPELPM_1\integration\orabpel\bin 디렉토리의 obsetenv.bat 파일과 C:\OraBPELPM_1\integration\orabpel\system\appserver\oc4j\j2ee\home\config 디렉토리의 application.xml 파일의 BASE_OB_CLASSPATH 환경 변수에 추가합니다.
  2. XMLBeansSerializer.class 파일(샘플코드 다운로드참고)을 C:\OraBPELPM_1\integration\orabpel\system\classes\com\oracle\bpel\xml\util 디렉토리에 복사합니다.
  3. XML Beans 컴파일러가 생성한 Java 클래스를 C:\OraBPELPM_1\integration\orabpel\system\classes 디렉토리에 복사합니다.
  4. ANT 유틸리티(obant)에서 사용하는 build.xml 파일에 xbean.jar의 경로를 추가합니다.
또 BPEL 프로젝트가 XML Bean serializer를 사용하도록 bpel.xml 파일에 정의를 추가해 주어야 합니다:
<partnerLinkBinding name="xmlBeansService">
  <property name="wsdlLocation">XMLBeansService.wsdl</property>
  <property name="javaSerializer">
        com.oracle.bpel.xml.util.XMLBeanJavaSerializer
  </property>
</partnerLinkBinding>
Java-to-XML 바인딩을 위한 세 번째 대안으로 Axis Bean이 있습니다. Axis Bean 역시 Oracle BPEL Process Manager에서 사용이 가능합니다.

Axis Bean을 사용하기 위한 설정 방법이 아래와 같습니다:
  1. Axis Beans JAR 파일(Axis 1.2의 axis.jar 파일)을 C:\OraBPELPM_1\integration\orabpel\bin 디렉토리의 obsetenv.bat 파일과 C:\OraBPELPM_1\integration\orabpel\system\appserver\oc4j\j2ee\home\config 디렉토리의 application.xml 파일의 BASE_OB_CLASSPATH 환경 변수에 추가합니다.

  2. AxisSerializer.zip 파일(샘플코드 다운로드)의 serializer 클래스들을 C:\OraBPELPM_1\integration\orabpel\system\classes 디렉토리에 복사합니다.

  3. Axis Bean에 의해 생성된 Java 클래스를 C:\OraBPELPM_1\integration\orabpel\system\classes 디렉토리에 복사합니다.
또, BPEL 프로젝트가 Axis Bean serializer를 사용하도록 bpel.xml 파일에 정의를 추가해 주어야 합니다:
<partnerLinkBinding name="AxisBeansService">
  <property name="wsdlLocation">AxisBeansService.wsdl</property>
  <property name="javaSerializer">
        com.oracle.bpel.xml.util.AxisJavaSerializer
  </property>
</partnerLinkBinding>
EJB를 위한 WSIF 바인딩

지금까지 WSIF 바인딩을 이용하여 웹 서비스 대신 Java 클래스를 이용하도록 설정하는 방법을 설명했습니다. 같은 방법으로 EJB, 특히 stateless 세션 빈(session bean)을 사용하도록 설정하는 것도 가능합니다. WSIF EJB 바인딩의 실습을 위해 샘플 코드를 확장해 보기로 합니다. 먼저 BPEL 프로세스에 새로운 액티비티를 추가하고, Book Rating 서비스를 호출한 뒤 Publisher Rating 서비스를 호출하도록 합니다. WSIF 바인딩을 이용하여 웹 서비스 대신 세션 빈을 사용하도록 설정합니다. 세션 빈은 이미 존재하는 것으로 가정합니다. 이를 위해 필요한 작업이 아래와 같습니다:

  • 세션 빈을 위한 WSDL을 정의합니다.
  • SDL에 partner link type을 추가합니다.
  • BPEL 프로세스가 Publisher Rating 서비스를 추가로 호출하도록 수정합니다.
  • WSIF 바인딩을 EJB에 추가합니다.
세션 빈을 위한 WSDL. 예제에서 사용할 세션 빈은 다음과 같은 원격 컴포넌트 인터페이스(remote component interface)를 갖습니다:
package com.oracle.ratingSB;

import java.rmi.RemoteException;
import javax.ejb.EJBObject;

public interface PubRating extends EJBObject
{
    public int getAvgPubRating (String name) throws RemoteException;

}
getAvgPubRating이라는 이름의 메소드를 주목하시기 바랍니다. getAvgPubRating 메소드는 string을 입력으로 받아 integer를 반환합니다. 여기에서는 home interface, implementation class, deployment descriptor에 대한 소개는 생략합니다 (샘플코드를 참고하십시오)

이제 WSDL 다큐먼트를 정의할 차례입니다. WSDL 다큐먼트는 아주 간단한 구조를 가지며, 두 개의 메시지(PubRatingRequestMessage, PubRatingResponseMessage)를 포함합니다. 메시지는 PubRating 오퍼레이션의 입력 및 출력으로 사용됩니다. PubRating 오퍼레이션은 PubRatingPT 포트 타입 내부에 정의됩니다:

<?xml version="1.0"?> 
<definitions xmlns:xs="http://www.w3.org/2001/XMLSchema" 
             xmlns:tns="http://oracle.com/service/pubrating/" 
             targetNamespace="http://oracle.com/service/pubrating/" 
             xmlns="http://schemas.xmlsoap.org/wsdl/"
             xmlns:plnk="http://schemas.xmlsoap.org/ws/2003/05/partner-link/"
             xmlns:format="http://schemas.xmlsoap.org/wsdl/formatbinding/"
             xmlns:ejb="http://schemas.xmlsoap.org/wsdl/ejb/" >
             
  <message name="PubRatingRequestMessage">
    <part name="name" type="xs:string" /> 
  </message>
  
  <message name="PubRatingResponseMessage">
    <part name="rating" type="xs:int" />
  </message>
  
  <portType name="PubRatingPT">
    <operation name="PubRating">
      <input name="PubRatingRequest" message="tns:PubRatingRequestMessage" /> 
      <output name="PubRatingResponse" message="tns:PubRatingResponseMessage" /> 
    </operation>
  </portType>
</definitions>
Partner Link Type의 추가. WSDL을 사용하기 위해서는 파트너 링크 타입(partner link type)을 추가해 주어야 합니다. 오퍼레이션은 동기식으로 수행되며, 따라서 하나의 role만 정의하면 됩니다:
<plnk:partnerLinkType name="PubRatingLT">
  <plnk:role name="PubRatingService">
    <plnk:portType name="tns:PubRatingPT" />
  </plnk:role>
</plnk:partnerLinkType>
BPEL 프로세스의 수정. 이제 BPEL 프로세스에 Publisher Rating 서비스를 호출하는 과정을 추가할 차례입니다. 먼저 새로운 파트너 링크를 정의합니다
<partnerLink name="PubRating" 
             partnerLinkType="pbr:PubRatingLT"
             partnerRole="PubRatingService"/>
다음으로, Book Rating 스코프(scope)의 바로 뒷부분에 Publisher Rating 서비스 호출을 위한 스코프를 추가합니다:
<scope name="RetrievePublisherRating">

  <variables>
      <variable name="PubRatingRequest" messageType="pbr:PubRatingRequestMessage"/>
      <variable name="PubRatingResponse" messageType="pbr:PubRatingResponseMessage"/>
  </variables>

  <faultHandlers>
      
      <catchAll>
      
        <sequence>
          <assign>
            <copy>
              <from expression="string('Unable to retrieve publisher rating')" />
              <to variable="Fault" part="error" />
            </copy>
          </assign>
        
          <invoke partnerLink="Client" 
                  portType="buy:ClientCallbackPT" 
                  operation="ClientCallbackFault"
                  inputVariable="Fault" />
        </sequence>
                      
      </catchAll>
      
  </faultHandlers>
        
  <sequence>

    <assign>
      <copy>
        <from variable="BookPurchase" part="book" query="/book/bkr:Publisher"/>
        <to variable="PubRatingRequest" part="name"/>
      </copy>
    </assign>
          
    <invoke partnerLink="PubRating" 
            portType="pbr:PubRatingPT" 
            operation="PubRating"
            inputVariable="PubRatingRequest" 
            outputVariable="PubRatingResponse" />

  </sequence>
    
</scope>
EJB를 위한 WSIF 바인딩의 추가. WSIF EJB 바인딩은 Java 클래스 바인딩과 유사하지만, WSDL 오퍼레이션을 EJB 메소드에 매핑하는 방법에서 차이를 갖습니다.

아래는 Publisher Rating 서비스 WSDL 파일의 WSIF EJB 바인딩에서 발췌한 내용입니다. 먼저 타입 매핑(type mapping)을 정의한 다음, PubRating 오퍼레이션을 위해 사용할 메소드를 지정해야 합니다(getAvgPubRating()). 매개변수(name)와 리턴(rating) 값을 위한 메시지 네임과 입력/출력을 위한 메시지 네임((PubRatingRequest, PubRatingResponse)이 각각 정의되어 있음을 참고하시기 바랍니다:

<binding name="EJBBinding" type="tns:PubRatingPT">
  <ejb:binding/>
  <format:typeMapping encoding="Java" style="Java">
    <format:typeMap typeName="xs:string" formatType="java.lang.String" />
    <format:typeMap typeName="xs:int" formatType="int" /> 
  </format:typeMapping>
  <operation name="PubRating">
    <ejb:operation
       methodName="getAvgPubRating"
       parameterOrder="name"
       interface="remote"
       returnPart="rating" />
    <input name="PubRatingRequest"/>
    <output name="PubRatingResponse"/>
  </operation>
</binding>
서비스 바인딩(services binding)에서는 EJB의 상세 정보(JNDI name, JNDI provider URL, initial context factory 등)가 정의됩니다. JNDI provider URL은 각 deployment 별로 달라지며, 예제의 경우 ormi://localhost/SessionBean으로 지정됩니다. obant 유틸리티를 이용하여 [jndiProviderURL]를 deploy 시점의 실제 주소로 대체할 수 있습니다 (자세한 사항은 build.xml 파일을 참고하십시오):
<service name="PubRatingPT">
  <port name="EJBPort" binding="tns:EJBBinding">
    <ejb:address className="com.oracle.ratingSB.PubRatingHome"
                 jndiName="ejb/session/PubRating"
                 initialContextFactory="com.evermind.server.rmi.RMIInitialContextFactory"
                 jndiProviderURL="[jndiProviderURL]"/> 
  </port>
</service>
이제 샘플 코드를 deploy하고 테스트할 준비가 거의 완료되었습니다. bpel.xml deployment descriptor에 wsdlLocation property를 추가하는 것을 잊지 마시기 바랍니다:
<partnerLinkBinding name="PubRating">
  <property name="wsdlLocation">PubRatingBinded.wsdl</property>
</partnerLinkBinding>
deployment descriptor를 이용하여 EJB의 인증 및 권한할당에 필요한 property들(java.naming.security.principal, java.naming.security.credentials 등)을 추가해 줄 수도 있습니다:
<partnerLinkBinding name="PubRating">
  <property name="wsdlLocation">PubRatingBinded.wsdl</property>
  <property name="java.naming.security.principal">admin</property>
  <property name="java.naming.security.credentials">welcome</property>	
</partnerLinkBinding>
샘플 코드를 테스트하기 위해, 먼저 세션 빈을 deploy하고 다음으로 BPEL 프로세스를 deploy합니다. 여기에서도 obant 유틸리티를 이용한 자동화가 가능합니다:
Z:\WSIF\4_JavaBindingEJB>obant

Z:\WSIF\4_JavaBindingEJB>SETLOCAL
Buildfile: build.xml

deploySessionBean:

build_ear:

deployIas:

deployOc4j:
     [java] Notification ==> Application Deployer for SessionBean STARTS [ 2005-
10-19T19:47:00.891CEST ]
     [java] Notification ==> Undeploy previous deployment
     [java] Notification ==> Copy the archive to C:\OraBPELPM_1\integration\orab
pel\system\appserver\oc4j\j2ee\home\applications\SessionBean.ear
     [java] Notification ==> Unpack SessionBean.ear begins...
     [java] Notification ==> Unpack SessionBean.ear ends...
     [java] Notification ==> Initialize SessionBean.ear begins...
     [java] Notification ==> Initialize SessionBean.ear ends...
     [java] Notification ==> Application Deployer for SessionBean COMPLETES [ 20
05-10-19T19:47:19.470CEST ]


bindingWsdl:
     [copy] Copying 1 file to Z:\WSIF\4_JavaBindingEJB

setJndiUrlOrclej2ee:

setJndiUrlIas:

setJndiUrlOc4j:
     [echo] Replacing token [jndiProviderURL] by ormi://virtualxp/SessionBean in
 Z:\WSIF\4_JavaBindingEJB/PubRatingBinded.wsdl

main:
    [bpelc] validating "Z:\WSIF\4_JavaBindingEJB\BuyBook.bpel" ...
    [bpelc] BPEL suitcase deployed to: C:\OraBPELPM_1\integration\orabpel\domain
s\default\deploy

all:

BUILD SUCCESSFUL
Total time: 28 seconds

Z:\WSIF\4_JavaBindingEJB>ENDLOCAL

Z:\WSIF\4_JavaBindingEJB>
콘솔에서 BPEL 프로세스를 실행하면, Publisher Rating 서비스가 호출되었음을 확인할 수 있습니다:

figure 4

EJB가 성공적으로 호출되었는지 분명하게 확인하기 위해, BPEL 서버 콘솔 윈도우에서 아래와 같은 실행 결과가 표시되는지 확인합니다:
05/10/19 19:50:51 Tutalii: C:\OraBPELPM_1\integration\orabpel\lib\orabpel.jar ar
chive
05/10/19 19:50:54 Avg. publisher rating for Packt Publishing: 5.
JDeveloper를 이용한 WSIF 바인딩의 생성

WSIF 바인딩을 수작업으로 생성하는 것은 매우 복잡한 작업입니다. Oracle JDeveloper 10.1.3(이 문서를 작성하는 시점에 Early Access Release가 제공되고 있습니다)은 Java 클래스, EJB와 같은 Java 리소스를 위한 WSDL과 WSIF의 자동 생성을 위한 마법사를 제공합니다. 이 마법사를 이용하면 BPEL로부터 Java 리소스를 호출하는 수고를 덜고, BPEL을 통합 작업에 보다 효과적으로 활용할 수 있습니다.

Java 클래스 또는 EJB를 기반으로 Java 웹 서비스를 생성하기 위해 마법사를 실행합니다. 아래 스크린샷은 Java 클래스를 이용한 경우의 실행 예를 보여 주고 있습니다:

figure 5

(Java EE 1.4와 호환하는) JAX-RPC 웹 서비스 타입을 선택합니다.

figure 6

다음으로, 사용하려는 Java 클래스 또는 stateless 세션 빈을 선택합니다. (WSIF 바인딩의 생성을 위해) WSIF 바인딩 옵션을 체크합니다.

figure 7

SOAP 메시지 포맷(Document/Wrapped, Document/Literal, RPC/Literal, RPC/Encoded) 중 하나를 선택합니다.

figure 8

XML 타입과 Java 클래스 간의 커스텀 매핑과 serializer를 정의합니다.

figure 9

웹 서비스가 사용할 네임스페이스를 설정합니다.

figure 10

마지막으로 WSDL을 통해 공개할 메소드를 선택합니다:

figure 11

그 밖에도 마법사를 통해 class loader, JAX-RPC handler classe, service state (stateful), 그리고 서비스에 의해 사용되는 기타 클래스 등을 정의할 수 있습니다. 대부분의 상황에서는 이와 같은 옵션을 설정할 필요가 없으므로, Finish 버튼을 클릭하여 마법사를 종료하고 생성된 WSDL과 WSDL 내부에 정의된 WSIF 바인딩을 확인합니다. 마법사에 대한 자세한 정보는 JDeveloper 제품문서를 참고하시기 바랍니다.

결론

지금까지 여러 가지 예제를 통해 설명한 것처럼, WSIF는 EAI 환경과 유사한 기능을 제공하고 BPEL 프로세스의 파트너 서비스를 쉽고 유연하게 구성할 수 있게 합니다. WSIF를 이용하여 BPEL의 적용범위를 기존 리소스 환경으로 확장하는 한편, SOA의 구현 과정에서 BPEL을 보다 유용하게 활용할 수 있습니다.


Matjaz JuricMatjaz B. Juric는 컴퓨터/정보과학 박사학위를 소지하고 있으며, Business Process Execution Language for Web Services (Packt Publishing)의 저자이기도 합니다. 그는 또 J2EE Design Patterns Applied, Professional J2EE EAI, Professional EJB, and NET Serialization Handbook을 공저하였으며, Web Services Journal, Java Developer's Journal 등의 전문지에 꾸준히 기고하고 있습니다.s.

여러분의 의견을 보내주십시오.

E-mail this page
Printer View Printer View