Java EE는 XML 문서 처리를 위한 다양한 기술을 제공하며, 이러한 기술에는JAXB(Java Architecture for XML Binding), StAX(Streaming API for XML), DOM(Document Object Model) API 등이 포함된다. 본 테크 팁에서는 이 기술들을 서로 비교하고, 예제 애플리케이션에서의 사용 실례를 살펴보도록 한다.
JAXB
JAXB 기술은 XML 스키마를 Java 오브젝트에 바인드하는 방법을 제공함으로써 개발자들이 각자의 자바 애플리케이션에서 손쉽게 데이터를 처리할 수 있도록 해준다. JAXB API는 XML 문서를 자바 오브젝트에 unmarshal하고 자바 오브젝트를 XML 문서에 marshal하는 메소드를 제공한다. JAXB에 관한 자세한 내용은 테크 팁 JAXB 2.0에 새로 추가된 기능을 참조하기 바란다.
JAXB를 사용하는 데 따른 중요한 이점은 스키마(또는 dtd)를 컴파일하여 자바 컨텐트 트리를 생성한 다음 플레인 자바 오브젝트로 작업할 수 있다는 점이다. JAXB의 경우에는 복잡한 스키마가 수반되는데, 소규모의 컨텐트만으로 작업하고자 하는 경우에는 그다지 적합하지는 않다.
StAX
StAX는 XML 문서를 처리하기 위한 스트리밍 API로, XML 문서를 읽고 쓰는 event-driven ‘pull’ 파서라 할 수 있다. StAX에 관한 자세한 내용은 테크 팁 Sun Java Streaming XML Parser 소개를 참조할 것.
StAX의 양방향 특성, 작은 메모리 풋프린트, 낮은 프로세서 요구사항 등은 JAXB나 DOM과 같은 API보다 더 유리한 점으로 작용한다. StAX는 대규모 문서에서 작은 양의 정보를 추출하는 데 특히 효과적이다. 반면에 StAX를 이용하는 데 따른 주된 단점은 문서의 뷰가 협소하다는 점인데, 따라서 사용자는 XML 문서를 읽기에 앞서 어떤 처리 작업을 수행할지 알고 있어야 한다. 또 다른 단점은 복잡한 스키마를 따르는 XML 문서를 리턴할 경우 StAX를 사용하기가 어렵다는 점이다.
DOM
DOM은 프로그램이 XML 문서의 내용을 동적으로 업데이트할 수 있게 해주는 플랫폼/언어 중립형 API이다. DOM에 관한 자세한 내용은 테크 팁 Using the Document Object Model(영문)을 참조할 것.
DOM은 전체 XML 문서의 in-memory 오브젝트 표현을 생성하는데, 이는 문서의 내용을 파싱, 네비게이트, 업데이트하는 데 엄청난 유연성을 제공해 준다. DOM의 단점은 메모리 요구사항이 높고 보다 강력한 처리 능력이 필요하다는 점이다.
예제 애플리케이션: JAXB, StAX, DOM의 비교
본 팁에는 예제 아카이브가 첨부되어 있으며, 이 예제 아카이브는 JAXB, StAX, DOM을 사용하여 웹 서비스에 대한 인풋으로 패스되는 XML 문서를 처리하는 과정을 예시한다. 예제 애플리케이션에서는 신용카드 인증에 대해 다루고 있다. 애플리케이션 클라이언트 코드는 웹 서비스를 호출하여 고객이 제시한 신용카드를 통한 결제를 인증하고, 클라이언트는 XML 문서에 포함된 신용카드 및 카드 소지자 정보를 웹 서비스에 전송한다. 또한 클라이언트는 JAXB, StAX, DOM을 사용하여 XML 문서를 처리할 수 있다. 예제는 이 세 가지 기술을 모두 사용한 XML 문서 처리 예시를 다루고 있다.
본 섹션에서는 예제 애플리케이션을 설치하고 실행하는 방법을 설명한다.
환경 설정하기
예제는 GlassFish라고 불리는 Java EE 5의 오픈 소스 레퍼런스 구현을 사용한다. GlassFish를 아직 구하지 못했다면 GlassFish 커뮤니티 다운로드 페이지에서 다운로드한다. 예제를 구축하고 실행하려면 JDK 5.0(J2SE 5.0 다운로드 페이지에서 다운로드할 수 있다)과 Apache Ant 1.6.5(GlassFish 번들에 포함되어 있으며, Windows의 경우에는 lib\ant 서브디렉토리에 위치함)도 함께 필요하다.
예제 애플리케이션 설치하기
예제 패키지를 다운로드하여 압축을 푼다. 예제를 위한 루트 디렉토리는 techtip이다. 현재 디렉토리를 techtip 디렉토리로 변경하고, 각 사용자의 빌드 환경을 반영하도록 스크립트 env.sh(UNIX의 경우) 또는 env.bat (Windows의 경우)를 편집한다. 예를 들어, 스크립트 내에 있는 JAVA_HOME 환경 변수의 값을 각자의 시스템에서 JDK 5.0의 위치로 변경한 다음 스크립트를 실행하여 각자의 환경을 설정하면 된다.
웹 서비스 구축하기
예제에서는 WSDL 파일을 이용하여 웹 서비스를 구축하는데, 이를 수행하기 위한 단계는 다음과 같다.
- XML 스키마를 기반으로 WSDL 파일을 생성한다.
- 엔드포인트 구현 클래스를 작성한다.
- 웹 서비스 실행을 위한 portable artifacts를 생성한다.
- 웹 서비스를 컴파일하고 WAR 파일로 패키징하여 배포한다.
XML 스키마를 기반으로 WSDL 파일을 생성한다
WSDL 파일은 예제 패키지에 포함되어 있으며, WSDL 파일 CreditCardService.wsdl은 conf 디렉토리에 들어 있다. WSDL 파일은 신용카드 결제를 인증하는 웹 서비스 연산을 보여준다. 애플리케이션에서 사용되는 세 가지 XML 처리 기술, JAXB, StAX, DOM 각각에 대해 별도의 연산이 수행된다. WSDL에 관한 자세한 내용은 Web Services Description Language (WSDL) 1.1을 참조할 것.
스키마 파일
WSDL 파일이 임포트하는 두 개의 스키마 파일은 CreditCardAuthorization.xsd와 CreditCardServiceException.xsd인데, CreditCardAuthorization.xsd는 클라이언트가 웹 서비스에 전송할 수 있는 XML 문서의 구조를 정의한다. 신용카드 인증 요청과 관련이 있는 스키마 내의 엘리먼트들은 다음과 같다.
<element name="AuthorizationRequest"
type="tns:AuthorizationRequest"/>
<element name="authorizePaymentSTAX" type="xsd:string"/>
<element name="authorizePaymentDOM">
<complexType>
<sequence>
<any maxOccurs="1"/>
</sequence>
</complexType>
</element>
타입 값 tns:AuthorizationRequest는 신용카드와 그 사용자에 관한 정보를 위한 엘리먼트를 가지는 complex type이다. StAX의 경우 엘리먼트 타입은 xsd:string이며, DOM의 경우 엘리먼트 타입은 xsd:any이다. 유사한 엘리먼트들이 정의되어 인증 상태를 리턴하는 데 사용된다.
CreditCardServiceException.xsd는 오류 메시지를 리턴하기 위한(즉, 웹 서비스가 요청을 처리할 수 없는 경우) XML 문서의 구조를 정의한다.
portable artifact 생성
예제에서는 wsimport 툴을 사용하여 SEI(Service Endpoint Interface), 서비스 클래스, JAXB 클래스, 예외 클래스 등과 같은 portable artifacts를 생성하는데, 타깃 이름이 generate-server인 ant 태스크에서 상기 작업을 수행하게 된다. 예제를 위한 ant 태스크는 techtip 디렉토리 내의 build.xml 파일에 들어 있다. portable artifacts를 생성하는 ant 태스크를 위한 XML은 다음과 같다.
<target name="generate-server" depends="prepare">
<wsimport
...
wsdl="${server.wsdl}">
<binding dir="${confdir}"
includes="${server.config}"/>
</wsimport>
</target>
위에 언급한 ant 변수는 conf 디렉토리 내의 build.properties 파일에서 정의된다. 이 때 <binding> 엘리먼트에 유의할 것. 이 엘리먼트는 conf 디렉토리에 들어 있는 외부 바인딩 파일 config-server.xml을 사용한다. JAXB의 경우, XML 문서를 위한 스키마를 해당 스키마를 나타내는 일련의 자바 인터페이스 및 클래스에 바인드한 다음 인터페이스와 클래스를 컴파일한다. config-server.xml 파일은 예제에서 사용하는 스키마를 위한 JAXB 바인딩을 정의한다. 스키마를 나타내는 인터페이스와 클래스를 컴파일하는 경우, GlassFish에는 JAXB의 바인딩 컴파일러 xjc를 내부적으로 호출하는 JAX-WS가 포함된다.
artifacts가 생성된 후에는 javac 컴파일러를 사용하여 컴파일을 수행한다. 예제에서는 타깃 이름이 compile-server인 ant 태스크에서 이 작업을 수행한다.
엔드포인트 구현 클래스 작성
예제에서 제공되는 엔드포인트 구현 클래스인 CreditCardService.java는 src\server 디렉토리에 들어 있다. CreditCardService.java는 전 단계에서 CreditCardService.wsdl과 wsimport 툴을 사용하여 생성한 SEI를 구현하는데, 이 클래스에는 다양한 문서 처리 기술을 위한 메소드들이 포함되어 있다.
다음은 JAXB를 사용하는 메소드이다.
public AuthorizationStatus authorizePaymentJAXB(
AuthorizationRequest parameters) throws
com.sun.techtip.cardservice.service.CardServiceException {
try {
CreditCard card = parameters.getCreditCard();
CardUser user = parameters.getCardUser();
// For sake of simplicity, read only limited
// information about credit card and credit card user
String cardNumber = card.getCardNumber();
String lastName = user.getLname();
// Emulate charge authorization
dao.authorizeCharge(cardNumber, lastName);
// Create and populate authorization status object
ObjectFactory of = new ObjectFactory();
AuthorizationStatus status =
of.createAuthorizationStatus();
status.setAuthorizationToken(dao.authorizationToken);
...
return status;
이 메소드는 JAXB가 생성한 오브젝트를 파라미터로 수신하며, 관련 JAXB 오브젝트 CreditCard 및 CardUser를 검색한다. 그런 다음 이 오브젝트들을 이용하여 신용카드 정보와 카드 사용자 이름 등과 같은 필요한 정보를 얻는다. 이어서 메소드는 애플리케이션 내의 비즈니스 로직을 호출하여 카드에 대한 대금 청구를 인증한다. dao 오브젝트는 src\server 디렉토리에 있는 CreditCardDAO 클래스의 인스턴스인데, 이 클래스는 인증 토큰, 대금 청구가 인증되었는지의 여부, 오류 코드(있을 경우) 등과 같은 정보를 제공한다. 끝으로, 메소드는 JAXB 오브젝트에 인증 상태를 파퓰레이트(populate)하고 리턴한다.
다음은 StAX를 사용하여 동일한 인증 태스크를 수행하는 메소드이다. 메소드에 패스되는 파라미터는 String 오브젝트(실제로는 XML 문서)이다.
public String authorizePaymentSTAX(String parameters) throws
CardServiceException {
try {
// Create the StAX reader to read the XML document
StringReader strReader = new StringReader(parameters);
XMLInputFactory staxFactory =
XMLInputFactory.newInstance();
staxFactory.setProperty(
XMLInputFactory.IS_VALIDATING, Boolean.FALSE);
XMLStreamReader xmlReader =
staxFactory.createXMLStreamReader(strReader);
StAX 파서는 XMLStreamReader를 사용하여 XML 문서를 읽어 들이고, XMLStreamWriter를 사용하여 이를 다시 쓴다. XMLStreamReader를 구성하는 데는 XMLInputFactory가 사용된다. 메소드 내의 코드의 다음 블록은 XML 문서를 traverse하고 이를 관련 엘리먼트들로부터 읽어 들인다.
// For sake of simplicity, we read only limited information
// about credit card and credit card user
String cardNumber= null;
String lastName = null;
while (xmlReader.hasNext()) {
if (xmlReader.isStartElement()) {
String sName = xmlReader.getLocalName();
if (sName.equals("cardNumber")) {
cardNumber = xmlReader.getElementText();
}
if (sName.equals("lname")) {
lastName = xmlReader.getElementText();
}
}
xmlReader.next();
}
xmlReader.close();
애플리케이션의 비즈니스 로직이 카드에 대한 대금 청구를 인증하고, 그런 다음 메소드가 응답 XML 문서를 생성한다. 아래 코드의 경우, XMLOutputFactory 오브젝트를 사용하여 XMLStreamWriter를 생성한다는 점에 유의할 것. XMLWriter는 응답 XML 문서를 작성하고, 웹 서비스 응답은 String 오브젝트로 재전송된다.
// Emulate charge authorization
dao.authorizeCharge(cardNumber, lastName);
// Create response XML document
StringWriter strWr = new StringWriter();
XMLStreamWriter resvStatusWr =
XMLOutputFactory.newInstance().createXMLStreamWriter(strWr);
resvStatusWr.writeStartDocument();
resvStatusWr.writeStartElement(
"ns0", "AuthorizationStatus", "urn:CardService");
resvStatusWr.writeStartElement(
"ns0", "authorizationToken", "urn:CardService");
resvStatusWr.writeCharacters(dao.authorizationToken);
resvStatusWr.writeEndElement();
...
resvStatusWr.writeEndDocument();
return strWr.toString();
다음은 DOM을 사용하는 메소드인데, 메소드에 대한 파라미터는 클라이언트가 전송하는 XML 문서의 루트 엘리먼트를 제공한다. 메소드는 이를 이용하여 문서를 파싱하고 신용카드와 그 사용자에 관한 필요한 정보를 수집한다.
public AuthorizeStatusDOM authorizePaymentDOM(
AuthorizePaymentDOM parameters) throws
CardServiceException {
try {
Element element = (Element)parameters.getAny();
// For sake of simplicity, we read only limited
// information about credit card and credit card user
Node cardNode = (
element.getElementsByTagName("cardNumber")).item(0);
String cardNumber =
cardNode.getFirstChild().getNodeValue();
Node lnameNode =
(element.getElementsByTagName("fname")).item(0);
String lastName =
lnameNode.getFirstChild().getNodeValue();
전과 마찬가지로, 메소드는 애플리케이션의 비즈니스 로직을 호출하여 카드 대금 청구를 인증하고, 이어서 SOAPElement 클래스를 이용하여 응답을 생성한다. root 엘리먼트를 생성하는 데는 SOAPFactory 오브젝트가 사용된다는 점에 유의할 것. 그런 다음 하위 엘리먼트에 인증 정보가 추가된다
// Emulate charge authorization
dao.authorizeCharge(cardNumber, lastName);
SOAPFactory sf = SOAPFactory.newInstance();
SOAPElement root = sf.createElement(
"AuthorizationStatus","ns0","urn:CardService");
SOAPElement token = sf.createElement("authorizationToken");
token.addTextNode(dao.authorizationToken);
root.addChildElement(token);
...
ObjectFactory of = new ObjectFactory();
AuthorizeStatusDOM status = of.createAuthorizeStatusDOM();
status.setAny(root);
return status;
웹 서비스를 컴파일하고 WAR 파일로 패키징하여 배포
앞서 CreditCardService.wsdl에서 wsimport를 실행하여 생성된 웹 서비스를 위한 portable artifacts와 더불어 서비스 구현 클래스를 컴파일해야 한다. 그런 다음 웹 서비스를 위한 배포 서술자 sun-jaxws.xml과 함께 클래스를 WAR 파일로 패키징해야 한다. 예제에서 이 단계는 타깃 build를 가지는 ant 태스크에 의해 수행된다.
클라이언트 구축
다음은 웹 서비스 클라이언트를 구축하기 위한 단계들이다.
- 클라이언트 클래스를 작성한다.
- 웹 서비스 실행을 위한 portable artifacts를 생성한다.
- 클라이언트를 컴파일한다.
클라이언트 클래스 작성
예제 패키지에는 독립형 웹 서비스 클라이언트 CreditCardServiceTest.java가 포함되어 있으며, src\client 디렉토리에 위치하고 있다. 클라이언트는 신용카드 웹 서비스를 호출하여 고객이 제시하는 신용카드에 대한 결제를 인증하고, 클라이언트는 XML 문서에 포함된 신용카드 및 카드 소지자 정보를 서버에 전송한다. 클라이언트는 JAXB, StAX, DOM을 사용하여 XML 문서를 처리할 수 있으며, 실제로 세 가지 기술을 모두 사용하게 된다.
다음은 JAXB를 이용하여 서비스를 호출하는 클라이언트 내의 메소드이다. JAXB unmarshaller는 XML 문서를 자바 오브젝트로 변환하고, 클라이언트는 Unmarshaller의 인스턴스를 이용하여 인증 요청을 위한 JAXB 오브젝트에 XML 문서를 unmarshal한 다음 웹 서비스를 호출한다.
public void testAuthorizePaymentJAXB(
String authrequestXMLfile) throws Exception {
try {
// Get the unmarshaller to convert XML into Object
JAXBContext jc =
JAXBContext.newInstance("creditcard");
Unmarshaller u = jc.createUnmarshaller();
// Unmarshal the XML document into authorization
// request object
JAXBElement cardAuthorizationElement =
(JAXBElement)u.unmarshal(
new FileInputStream(authrequestXMLfile));
AuthorizationRequest cardAuthorizationRequest =
(AuthorizationRequest)
cardAuthorizationElement.getValue();
// Make web service call
AuthorizationStatus reply =
stubC.authorizePaymentJAXB(cardAuthorizationRequest);
서비스로부터의 응답이 JAXB 인증 상태 오브젝트라는 점에 유의할 것. 또한, stubC는 서비스 엔드포인트 인터페이스 상에 메소드를 호출하는 데 사용되는 프록시인데, 이는 클라이언트 내의 모든 XML 처리 메소드에서 사용된다. 프록시는 CreditCardServiceTest 클래스의 생성자에서 초기화된다.
클라이언트는 XML 문서를 사용하는 대신 관련 JAXB 오브젝트를 파퓰레이트한 다음 서비스를 호출할 수도 있는데, 이런 방법은 CardServiceTest.java의 testAuthorizePaymentJAXB() 메소드에서 찾아볼 수 있다.
다음은 StAX를 이용하여 서비스를 호출하는 클라이언트 내의 메소드이다. 이 메소드는 XML 문서를 String 오브젝트의 형태로 서비스에 패스하는데, 서비스로부터의 응답 역시 String이다. readStringFromFileRaw 메소드는 XML 파일을 읽고 String을 리턴하는 유틸리티 메소드이다.
public void testAuthorizePaymentSTAX(
String authrequestXMLfile) throws Exception {
// Read XML document into a string
String authRequest =
readStringFromFileRaw(authrequestXMLfile);
try {
// Make web service call
String reply =
stubC.authorizePaymentSTAX(authRequest);
끝으로, DOM을 사용하여 웹 서비스를 호출하는 클라이언트 내의 메소드이다. 유틸리티 메소드 createElementMessage는 XML 파일을 읽은 후에 문서의 루트 엘리먼트를 리턴한다. 이 때, 클라이언트는 웹 서비스 호출에 문서의 루트 엘리먼트를 이용하며, 서비스로부터의 응답은 DOM traversal API를 통해 디스플레이될 수 있다.
public void testAuthorizePaymentDOM(String authrequestXMLfile)
throws Exception {
// Get document root element
Element authRequest =
createElementMessage(authrequestXMLfile);
try {
// Populate authorization request object
ObjectFactory of = new ObjectFactory();
AuthorizePaymentDOM msg =
of.createAuthorizePaymentDOM();
msg.setAny(authRequest);
// Make web service call
AuthorizeStatusDOM reply =
stubC.authorizePaymentDOM(msg);
portable artifact 생성
서버 상의 WSDL을 사용하여 클라이언트 측 portable artifacts를 생성한다. 예제에서는 타깃 build를 가지는 ant 태스크를 사용하여 이를 수행하는데, 실제로 ant 타깃 build를 호출하면 생성된 artifacts를 포함하여 서버 및 클라이언트 클래스를 모두 컴파일하게 된다. 이와 더불어 배포 가능한 WAR 파일을 구축한다. 단, 클라이언트가 JAXB 바인딩 구성 파일 config-client.xml을 사용한다는 점에 유의할 것.
예제 실행하기
예제를 실행하려면 다음 단계를 수행한다.
- 다음 명령어를 입력하여 GlassFish를 시작한다.
%J2EE_HOME%/bin/asadmin start-domain domain1
J2EE_HOME환경 변수가 GlassFish 설치 디렉토리로 설정되어 있는지 확인한다.
- 웹 서비스, 클라이언트, 그리고 필요한 artifacts를 구축하고 다음 명령어를 입력하여 이를 WAR 파일로 패키징한다.
ant build
- 다음 명령어를 입력하여 애플리케이션을 배포한다.
ant deploy
- 다음 명령어를 입력하여 애플리케이션을 실행한다.
ant run
명령어는 세 가지 XML 처리 기술을 모두 사용하여 애플리케이션을 실행하게 되는데, 아래와 유사한 결과가 표시되어야 한다.run: [java] Calling Credit Card Service using JAXB [java] Authorization token = 84706723 [java] Authorized = true [java] Error code = 0 [java] Calling Credit Card Service using STAX [java] <?xml version="1.0" ?><ns0:AuthorizationStatus> <ns0:authorizationToken>39114069</ns0:authorizationToken> <ns0:authorized>true</ns0:authorized><ns0:errorCode>0 </ns0:errorCode></ns0:AuthorizationStatus> [java] Calling Credit Card Service using DOM [java] authorizationToken = 66927959 [java] authorized = true [java] errorCode = 0 - 다음 명령어를 입력하여 애플리케이션을 배포 해제한다.
ant undeploy
- 다음 명령어를 입력하여 애플리케이션을 위해 생성된 모든 클래스와 WAR 파일을 삭제한다.
ant clean
글쓴이 소개
Deep Singh은 썬 마이크로시스템즈 Java Performance Engineering 그룹의 스태프 멤버로 활동하고 있다.
"Java EE" 카테고리의 다른 글
- Attach API (댓글 18개 / 트랙백 1개) 2007/09/03
- JSP 2.0 EXPRESSION LANGUAGE (댓글 1개 / 트랙백 0개) 2004/02/05
- 환경 엔트리를 이용해서 배포의 사용자 정의하기 (댓글 2개 / 트랙백 0개) 2003/12/24
- JAX-WS를 이용한 웹 서비스 개발 (댓글 1개 / 트랙백 0개) 2006/01/18
- EJB 2.1로 메시지 구동 빈 이용하기 (댓글 1개 / 트랙백 0개) 2005/05/18
- EclipseLink를 사용하여 JPA에서 반복 불가능한 읽기 방지 (댓글 0개 / 트랙백 1개) 2008/07/09
- 컴포넌트 시스템과 클래스 로더 경계 (댓글 1개 / 트랙백 0개) 2004/10/05
- JAX-WS Dispatch 및 Provider API를 이용한 문서 처리 (댓글 4개 / 트랙백 0개) 2006/09/15
- POJO를 Persistent Entity로 변환하기 (댓글 1개 / 트랙백 0개) 2005/12/27
- SAAJ 소개 (댓글 1개 / 트랙백 0개) 2005/06/08
댓글을 달아 주세요
좋은 정보 감사해요~
2007/09/19 04:20유용한 정보 자료 감사합니다
2007/09/19 13:27