상세 컨텐츠

본문 제목

JavaTM Web Services Developer Pack 2.0 (2)

프로그래밍/웹서비스

by 라제폰 2008. 12. 13. 20:27

본문

JAXB 2.0의 추가 기능
 
JAXB(Java Architecture for XML Binding) XML 스키마로부터 클래스 데이터를 바인딩, XML로부터 객체를 직렬화(Serialization) 하거나 마샬링, 언마샤링을 수행 할 수 있도록 지원해준다. JWSDK 2.0에 포함되어있는 JAXB 2.0이며 이전 버전에 비해 다음 기능이 추가되었다. 이전 버전에 관한 내용은 http://blog.empas.com/inter999/6549631을 참고하시기 바랍니다.
  • 모든 W3C XML 스키마 기능에 대한 지원. JAXB 1.0에서는 W3C XML 스키마의 일부 기능에 대해 바인딩이 지정되지 않았다. 이는 FCS 릴리즈에서 완벽하게 지원될 예정이지만, 현재의 얼리 액세스 버전에서는 아직 지원 수준이 미흡한 상태이다.
  • Java-to-XML 바인딩 지원, 그리고 이 바인딩을 제어하기 위한 javax.xml.bind.annotation 패키지 추가. JAXB 1.0에서는 XML Schema-to-Java의 매핑은 지정되었지만 Java-to-XML Schema의 매핑은 지정되지 않았다.
  • 생성되는 스키마 파생 클래스의 수 대폭 감소.
  • JAXP 1.3 검증 API를 통한 추가 검증 기능 제공.
  • 더 작아진 런타임 라이브러리.
Sample Program 작성
JAXB 1.x을 설명한 http://blog.empas.com/inter999/6549631을 비교하시며 보시길 바랍니다.
 
user.xsd 작성
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="userinfo" type="Userinfo"/>
<xsd:complexType name="Userinfo">
  <xsd:sequence>
    <xsd:element name="name" type="xsd:string"/>
    <xsd:element name="age" type="xsd:int"/>
    <xsd:element name="phone" type="xsd:int"/>
    <xsd:element name="memberLevel" type="xsd:string"/>
  </xsd:sequence>
</xsd:complexType>
</xsd:schema>
 
XML 스키마에서 Java 클래스 생성
C:jaxb>set path=%PATH%;C:jwsdp-2.0jaxbbin;
C:jaxb>xjc -p member.user.jaxb -d . user.xsd
parsing a schema...
compiling a schema...
memberuserjaxbObjectFactory.java
memberuserjaxbUserinfo.java
C:jaxb>
JAXB 1.x 버전에서는 Java 클래스가 20개 이상 생성되었지만 2.0에서는 스키마 생성 클래스가 대폭 줄어들어 클래스가 2개만 생성됨을 볼 수 있다. 파생된 Java 클래스를 컴파일 하기위해 JWSDP_HOME/jaxb/lib, JWSDP_HOME/jwsdp_shared/lib 하위의 jar 파일을 classpath에 추가하고 컴파일 한다.
C:jaxb>set classpath=.;C:jwsdp-2.0jaxblibjaxb-xjc.jar;C:jwsdp-2.0jaxblib
jaxb-api.jar;C:jwsdp-2.0jaxblibjaxb-impl.jar;C:jwsdp-2.0jaxblibjaxb1-im
pl.jar;C:jwsdp-2.0jwsdp-sharedlibmail.jar;C:jwsdp-2.0jwsdp-sharedlibacti
vation.jar;C:jwsdp-2.0jwsdp-sharedlibjaas.jar;C:jwsdp-2.0jwsdp-sharedlib
jta-spec1_0_1.jar;C:jwsdp-2.0jwsdp-sharedlibrelaxngDatatype.jar;C:jwsdp-2.0
jwsdp-sharedlibxsdlib.jar;C:jwsdp-2.0jwsdp-sharedlibxmlsec.jar;C:jwsdp-2
.0jwsdp-sharedlibresolver.jar
 
C:jaxb>javac -d . member/user/jaxb/*.java
 
Server Program 작성
 
userSVR.java
package member.user.jaxb;
import java.io.ByteArrayInputStream;
import java.io.CharArrayWriter;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.PrintWriter;
import java.io.IOException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import org.w3c.dom.Node;
 
import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Schema;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.ValidationEventLocator;
 
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
 
public class userSVR extends HttpServlet  {
    private static final String CONTENT_TYPE = "text/html; charset=EUC-KR";
    private String name = "";
    private int age = 0;
    private int Phone = 0;
    private String level = "Administrator";
 
    public void init(ServletConfig config)
    throws ServletException {
        super.init(config);
    }
 
    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        doProcess(request, response);
    }
 
    public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        doProcess(request, response);
    }
   
    public void doProcess(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        response.setContentType(CONTENT_TYPE);
        PrintWriter out = response.getWriter();
        String message = request.getParameter("message");
       
        System.out.println(message);
       
        try {
            unmarshal(message);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
       
        try {
            marshal(out);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        out.close();
    }
 
   
    public void marshal(PrintWriter out)
    throws Exception {
 
        CharArrayWriter charArrayWriter = new CharArrayWriter();
       
        ObjectFactory objFactory = new ObjectFactory();
        Userinfo userinfo = objFactory.createUserinfo();
 
       
        userinfo.setName(name);
        userinfo.setAge(age);
        userinfo.setPhone(Phone);
        userinfo.setMemberLevel(level);
        JAXBElement<Userinfo> userElement = objFactory.createUserinfo(userinfo);
 
        JAXBContext jaxbContext = JAXBContext.newInstance("member.user.jaxb");
        Marshaller marshaller = jaxbContext.createMarshaller();
       
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, new Boolean(true));
        marshaller.marshal(userElement, charArrayWriter);
        String xmlOutput = charArrayWriter.toString();
        System.out.println(" Send XML : "+xmlOutput);
        out.println(xmlOutput);
 
    }
 
    public void unmarshal(String message) throws Exception {
        ByteArrayInputStream bais =
            new ByteArrayInputStream(message.getBytes());
        JAXBContext jc = JAXBContext.newInstance("member.user.jaxb");
        Unmarshaller unmarshaller = jc.createUnmarshaller();
 
// 검증 부분
                           try {
                                        SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
                                        Schema schema = sf.newSchema(new File("user.xsd"));
 
                                        unmarshaller.setSchema(schema);
                                        unmarshaller.setEventHandler(
                    new ValidationEventHandler() {
                        public boolean handleEvent(ValidationEvent ve) {
                            if (ve.getSeverity() != ValidationEvent.WARNING) {
                                ValidationEventLocator vel = ve.getLocator();
                                System.out.println("Line:Col[" + vel.getLineNumber() +
                                    ":" + vel.getColumnNumber() +
                                    "]:" + ve.getMessage());
                            }
                            return true;
                        }
                    }
                                        );
                           } catch (org.xml.sax.SAXException ex) {
                                        System.out.println(ex.getMessage());
                                        ex.printStackTrace();
                           }
                          
                           JAXBElement<?> poElement =
                                       JAXBElement<?>)unmarshaller.unmarshal(bais);
                           Userinfo instance = (Userinfo)poElement.getValue();
 
        name = instance.getName();
        age = instance.getAge();
        Phone = instance.getPhone();
       
        System.out.println("##########################################");
        System.out.println("user JAXB GET DATA -->");
        System.out.println("Name : "+name);
        System.out.println("AGE : "+age);
        System.out.println("Phone : "+Phone);
    }
}
marshal, unmarshal에서 JAXBElement” 이용한다. 가장 크게 바뀐 부분은 검증 부분이다.
JAXB 2.0에서는 JAXP 1.3 스키마 검증 프레임워크를 이용하여 검증 기능을 보다 향상시켰다.이처럼 강화된 레벨의 검증을 이용하려면 먼저 W3C XML Schema 1.0 언어를 위한 SchemaFactory 인스턴스를 생성한다.
 
SchemaFactory sf =
SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
 
 
SchemaFactory 인스턴스는 새로운 JAXP 1.3 Schema 객체를 생성하는 데 사용된다.
 
Schema schema = sf.newSchema(new File("user.xsd"));
 
그런 다음 setSchema 메소드를 이용하여 이어지는 언마샬링 동작을 검증하는 데 사용할 JAXP 1.3 Schema 객체(이 경우에는 user.xsd)를 식별한다. 한편, 이 메소드에 null을 패스하면 검증 기능은 디스에이블("사용하지 않음") 상태가 된다.
 
unmarshaller.setSchema(schema);
 
애플리케이션은 기본값 이벤트 핸들러를 오버라이드하여 검증 오류 처리를 커스터마이즈할 수 있는 옵션을 가지는데, 이 경우에는 메소드 콜 setEventHandler(ValidationEventHandler)가 사용된다.
 
JAXB 1.0으로 언마샬링을 하는 이전의 접근법(소위 '구조적 언마샬링')은 유효하지 않은 XML 컨텐츠에 대해 더 엄격한 편이다. JAXB 1.0은 복구 불가능한 구조적 불일치가 탐지되면 언마샬링 프로세스를 취소하고 UnmarshalException throw한다. 실례로, 예상치 않은 엘리먼트나 속성에 마주치면 언마샬링을 중단할 수 있다.
 
이와 달리 JAXB 2.0은 보다 유연한 언마샬링을 허용하는데, 이는 부적절한 XML 컨텐츠의 언마샬링 과정에서의 예측 가능성을 더욱 높여준다. JAXB 2.0의 경우, 컨텐츠 모델 내의 엘리먼트 위치를 기준으로 하여 엄격하게 적용시키기 보다는 엘리먼트 이름 별로 xml을 언마샬링한다.
 
각자의 커스텀 이벤트 핸들러를 지정하는 방법은 다음과 같다.
 
   unmarshaller.setEventHandler(new DefaultValidationEventHandler());
 
커스텀 검증 이벤트 핸들러는 반드시 ValidationEventHandler 인터페이스와 handleEvent 메소드를 구현해야 한다. JAXB Provider가 경고 또는 오류를 처리한 후에 현재의 언마샬링, 검증, 또는 마샬링 연산을 계속하려고 시도할 경우에는 불(boolean) 리턴 값이 true로 설정되어야 한다. 프로바이더가 적절한 UnmarshalException, ValidationException, 또는 MarshalException으로 현재의 연산을 종료할 경우에는 리턴 값이 false로 설정되어야 한다. JAXB 2.0의 기본값 ValidationHandler는 항상 true를 리턴한다는 점에 유의할 것(, handleEvent가 오버라이드되지 않아야 함). 다음은 커스텀 이벤트 핸들러의 예제이다.
 
public class MyValidationEventHandler implements
    ValidationEventHandler{
    
      public boolean handleEvent(ValidationEvent ve) {           
        if (ve.getSeverity()==ve.FATAL_ERROR || 
                               ve .getSeverity()==ve.ERROR){
            ValidationEventLocator  locator = ve.getLocator();
            //print message from valdation event
            System.out.println("Message is " + ve.getMessage());
            //output line and column number
            System.out.println("Column is " +
                  locator.getColumnNumber() +
                  " at line number " + locator.getLineNumber());
         }
         return true;
       }
   
   }
 
JAXB 스펙은 오류 발생 시 모든 프로바이더 구현이 검증 오류를 보고할 것을 요구하고 있지만, 구현이 데이터 프로세싱을 중단할 필요까지는 없다. 한편, XML 문서가 유효하지 않더라도 JAXB 구현은 XML 문서를 성공적으로 언마샬링 할 수 있다.
 
po.xml이 언마샬링된 후에는 마샬링된 객체를 다른 자바 객체와 마찬가지로 처리할 수 있다. 예를 들어, shipTo 주소를 디스플레이하려면 다음 절차를 따르도록 한다.
 
JAXBElement<?> poElement =
   JAXBElement<?>)u.unmarshal(
      new FileInputStream( "po.xml" ) );
   PurchaseOrderType po = (
      PurchaseOrderType)poElement.getValue();
   USAddress address = po.getShipTo();
 
컨텐츠를 다시 XML 파일로 마샬링하려면 JAXB 1.0과 동일한 단계를 거치도록 한다. (앞서 언마샬링 과정에서 본 것처럼) JAXBContext 객체를 생성한 후에는, 마샬링 프로세스를 제어하는 Marshaller 객체를 생성한다. 예를 들어, 다음의 코드 단편은 Marshaller를 생성하여 컨텐츠를 incorrectpo.xml이라는 이름의 파일로 마샬링하는 경우를 보여주고 있다.
 
Marshaller m = jc.createMarshaller();
        m.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT,
              Boolean.TRUE );
        OutputStream os = new FileOutputStream(
              "incorrectpo.xml" );
        m.marshal(po, os);
 
JAXB.FORMATTED.OUTPUT 속성은 Marshaller가 출력된 XML 데이터를 줄바꿈과 들여쓰기로 포맷할 것인지 여부를 제어한다.
 
본 팁의 예제 코드에는 표시되어 있지 않지만, JAXB 2.0은 마샬링 시에(언마샬링 시에도) 검증을 지원한다. 한편, JAXB 1.0의 경우에는 언마샬링 시에만 검증을 규정하고, JAXB 컨텐츠 트리의 온디맨드 검증을 지원하는데, 마샬링 시의 검증은 웹 서비스 때문에 추가된 것이다. 웹 서비스 프로세싱 모델은 데이터를 읽어들일 때는 어느 정도 느슨하고 반대로 데이터를 기록할 때는 엄격해야 한다. 이 모델에 부합하도록, JAXB 2.0은 사용자가 xml 문서를 JAXB 양식으로 수정할 경우 문서가 무효화되지 않도록 하기 위해 마샬링 시의 검증을 추가하였다.
 
Servlet을 컴파일 한다.
C:jaxb>set classpath=.;C:jwsdp-2.0jaxblibjaxb-xjc.jar;C:jwsdp-2.0jaxblib
jaxb-api.jar;C:jwsdp-2.0jaxblibjaxb-impl.jar;C:jwsdp-2.0jaxblibjaxb1-im
pl.jar;C:jwsdp-2.0jwsdp-sharedlibmail.jar;C:jwsdp-2.0jwsdp-sharedlibacti
vation.jar;C:jwsdp-2.0jwsdp-sharedlibjaas.jar;C:jwsdp-2.0jwsdp-sharedlib
jta-spec1_0_1.jar;C:jwsdp-2.0jwsdp-sharedlibrelaxngDatatype.jar;C:jwsdp-2.0
jwsdp-sharedlibxsdlib.jar;C:jwsdp-2.0jwsdp-sharedlibxmlsec.jar;C:jwsdp-2
.0jwsdp-sharedlibresolver.jar;
 
C:jaxb>javac -d . member/user/jaxb/*.java
 
작성한 Servlet jaxb20test라는 Webapplication에 배포하고 web.xml파일에 등록하였다. XML 스키마에서 생성된 Java 클래스도 Server모듈의 ClassPath에 등록해준다. 여기서는 WEB-INF/classes 디렉토리에 패키지를 설치 하였다.
 
web.xml
<?xml version = '1.0' encoding = 'EUC-KR'?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
  <description>Empty web.xml file for Web Application</description>
  <servlet>
    <servlet-name>userSVR</servlet-name>
    <servlet-class>member.user.jaxb.userSVR</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>userSVR</servlet-name>
    <url-pattern>/usersvr</url-pattern>
  </servlet-mapping>
  <session-config>
    <session-timeout>35</session-timeout>
  </session-config>
  <mime-mapping>
    <extension>html</extension>
    <mime-type>text/html</mime-type>
  </mime-mapping>
  <mime-mapping>
    <extension>txt</extension>
    <mime-type>text/plain</mime-type>
  </mime-mapping>
</web-app>
 
Server Application 확인
http://localhost:8080/jaxb20test/usersvr 을 접속하였을 때 아래와 같은 화면이 출력되면 정상적으로 Server 어플리케이션이 설치 된 것이다.
 
 
Client Program
 
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
 
public class jaxb20Client {
    public jaxb20Client() throws Exception {
 
        StringBuffer buffer = new StringBuffer();
        buffer.append("<?xml version="1.0" encoding="UTF-8" standalone="yes"?>");
        buffer.append("<userinfo> n");
        buffer.append("<name>inter999</name> n");
        buffer.append("<age>32</age> n");
        buffer.append("<phone>5298989</phone> n");
        buffer.append("<memberLevel>tester</memberLevel> n");
        buffer.append("</userinfo>");
   
        URL url =
            new URL("http","localhost",
                    8080,
                    "/jaxb20test/usersvr?message="+URLEncoder.encode(buffer.toString()));
        URLConnection urlcon = url.openConnection();
               
        InputStream in = urlcon.getInputStream();
        BufferedReader inre = new BufferedReader(new InputStreamReader(in));
        String message = "";
        while((message = inre.readLine()) != null)
            System.out.println(message);
       
    }
   
    public static void main(String args[]) throws Exception {
        jaxb20Client client = new jaxb20Client();
    }
}
 
실행
Web Application을 구동하고 작성한 클라이언트 프로그램을 실행한다.
 

 


관련글 더보기