오픈 소스 API로 XML 문서 조작을 단순하게


난이도 : 중급

Wes Biggs, Senior Developer, T.H.I.
Harry Evans, Senior Developer, T.H.I.

2007 년 8 월 28 일


JDOM은 XML과 함께 작동하는 고유한 자바 툴킷으로서, XML 애플리케이션의 신속한 개발을 목적으로 설계되었습니다. JDOM의 디자인에는 자바 언어의 신택스부터 의미까지 포괄되어 있습니다. 하지만, 기존의 XML API보다 과연 얼마나 나을까요? 여러분이 직접 예제를 실행해 보고, 오픈 소스 프로젝트의 디자인 목표와 비교하면서 판단해 봅시다.



개발자인 여러분들도 80-20 규칙에 대해 들어봤을 것이다. 프로세스나 방법론이 모든 상황의 80 퍼센트를 차지하고, 나머지 20 퍼센트는 상황 별로(case-by-case) 다루어져야 한다. 소프트웨어 개발 세계에서 개발자가 주어진 기술을 사용하여 수행할 수 있는 일들의 80 퍼센트를 이룩하기는 매우 쉽다.

물론, 소프트웨어 제품과 표준이 80-20 규칙을 늘 따르는 것은 아니다. 특히 자바 XML 툴의 어긋난 부분은 이 규칙의 예외를 증명하고 있다. 자바 프로그래밍 세계는 수 많은 API들로 가득 차있다. 어떤 것은 자생한 것이고, 어떤 것은 대기업의 마케팅의 지원을 받기도 한다. XML의 보편성에 대한 약속으로서, 모든 새로운 태스크에는 새로운 기술이 있다. 하지만, 무엇이 접착제 역할을 하고, 여러분은 80 퍼센트의 작업을 수행하는데 적합한 툴을 어떻게 찾겠는가? JDOM은 이러한 질문을 염두 해 두고 구현된 XML API이다.

자바와 XML

소셜 북마크

mar.gar.in
Digg
del.icio.us
Slashdot

여러 가지 면에서, 자바 언어는 XML을 위한 프로그래밍 언어가 되었다. Apache Software Foundation과 IBM alphaWorks의 노력으로 XML 문서의 생성, 조작, 변형, 파싱을 위한 완전한 툴 체인이 생겼다.

많은 자바 개발자들은 XML을 매일 사용하지만, Sun은 XML을 자바 플랫폼에 적용하는데 뒤쳐졌다. Java 2 플랫폼은 XML이 비즈니스 대 비즈니스 통합부터 웹 사이트 콘텐트 스트리밍에 이르기까지 모든 것에 대한 핵심 기술이 되기 전에 가치 있는 것이 되었기 때문에 Sun은 폭넓은 채택성을 획득했던 기존 XML API의 창시자가 되는 JSR 프로세스를 사용해왔다. 가장 중요한 것은 JAXP(Java API for XML Parsing)의 추가이다. 여기에는 세 개의 패키지들이 포함된다.

  • org.w3c.dom: XML을 위한 표준 프로그래밍 방식의 Document Object Model의 W3C recommendation의 자바 구현.
  • org.xml.sax: XML 파싱을 위한 이벤트 중심의 API.
  • javax.xml.parsers: 애플리케이션 개발자들이 특정 파서 구현을 설정하고 획득할 수 있도록 해주는 팩토리 구현.

이러한 패키지들의 추가가 자바 개발자들에게는 좋은 일이지만, 고급의 자바-XML 상호 운용성으로 큰 도약을 이룬 것이 아닌 기존 API 표준에 대한 일반적인 순응을 나타낸다. 핵심 자바 플랫폼에서 부족한 것은 XML 문서를 자바 객체로서 조작할 수 있는 매력적인 인터페이스이다.

JDOM을 생각해 보자. 유명한 자바 개발자이자 작가인 Brett McLaughlin과 Jason Hunter의 생각의 산물인 JDOM은 2000년 초반에 Apache 계열의 라이센스 하에서 오픈 소스 프로젝트로서 시작되었다. 폭넓은 자바 개발자 베이스로부터 기여와 피드백, 버그 픽스를 받아들였고, 자바 코드에서 XML 데이터에 액세스 하여, 조작 및 결과를 만들어 내는 완벽한 자바 플랫폼 기반의 솔루션 구현을 목표로 설정했다.




위로


API로서의 JDOM

JDOM은 XML 문서들을 프로그래밍 방식으로 조작하는 org.w3c.dom 패키지에 대한 대안으로서 사용될 수 있다. 완벽한 대체는 아니고, 사실, JDOM과 DOM은 공존할 수 있다. 게다가, JDOM은 텍스트 인풋에서 XML을 파싱하는 것을 신경 쓰지 않는다. 파서 구현을 설정 및 실행하는데 도움이 되는 래퍼 클래스를 제공하기도 한다. JDOM은 기존 API를 기반으로 구현되었다.

대안 API의 필요성을 이해하려면, W3C DOM의 디자인 제약 조건에 대해 살펴보자.

  • 언어 독립성. DOM은 자바 언어를 염두 해 두고 설계되지 않았다. 이것의 접근 방식은 다양한 언어들 사이에서 매우 비슷한 API를 유지하지만, 자바의 이디엄에 익숙한 프로그래머에게는 성가신 API이다. 예를 들어, 자바 언어는 String 클래스가 언어에 구현되어 있지만, DOM 스팩은 고유의 Text 클래스를 정의한다.

  • 엄격한 계층. DOM의 API는 XML 스팩을 따른다. 따라서, 모든 것의 노드가 되는 XML에서, 모든 것이 확장하는 DOM에서 Node 기반 인터페이스와 Node를 리턴하는 메소드의 호스트를 찾는다. 이는 다형성의 관점에서 볼 때는 뛰어나지만, 자바 언어로 작업하기에는 불편하다. Node에서 리프(leaf) 유형으로의 변화는 장황하고 이해하기 어려운 코드를 만든다.

  • 인터페이스 중심. 퍼블릭 DOM API는 인터페이스들로만 구성된다. (한 가지 예외는 Exception 클래스이다.) W3C는 인터페이스를 정의할 때 구현을 제공하는 것에는 관심이 없다. 자바 프로그래머로서 API를 사용한다는 것은 XML 객체들을 생성할 때 어느 정도의 분리가 된다. W3C 표준은 일반적인 팩토리 클래스와 유연하지만 덜 직접적인 패턴들을 사용하기 때문이다. XML 문서들이 애플리케이션 레벨 코드가 아닌 파서에 의해서만 구현되는 특수한 경우, 이는 무관하다. 하지만, XML 사용이 널리 퍼지면서, 애플리케이션 개발자들은 XML 객체들을 프로그래밍 방식으로 구현하는 편리한 방법을 필요로 하게 되었다.

프로그래머에게, 이러한 제약 조건은 (메모리 사용과 인터페이스 규모 면에서) 무겁고 다루기 어렵다는 것을 의미한다. 반대로, JDOM은 자바 중심의, 경량의 API이다. DOM의 원리를 조정하여 위에 언급한 불편함을 해소시켰다.

  • JDOM은 자바 플랫폼 식이다. 이 API는 자바 언어의 빌트인 String 지원을 사용하기 때문에, 텍스트 값은 String으로서 언제나 사용할 수 있다. ListIterator 같은 Java 2 플랫폼 컬렉션 클래스도 활용하면서 자바 언어에 익숙한 프로그래머에게 풍부한 환경을 제공한다.

  • 계층이 없음. JDOM에서, XML 엘리먼트는 Element의 인스턴스이고, XML 애트리뷰트는 Attribute의 인스턴스이며, XML 문서는 Document의 인스턴스이다. 이 모든 것들이 XML에서 다른 개념들을 나타내기 때문에, 무정형의 "노드"로서가 아닌 고유의 유형으로서 참조된다.

  • 클래스 중심. JDOM 객체는 Document, Element, Attribute 같은 클래스들의 직접적인 인스턴스이므로, 이를 구현하는 것은 자바 언어에서 new 연산자를 사용하는 것만큼이나 쉽다. 또한 설정 할 팩토리 인터페이스가 없다. JDOM은 jar에서 직접 사용할 준비가 되어있다.



위로


JDOM 문서 구현 및 조작

JDOM은 표준 자바 코딩 패턴을 사용한다. 가능하다면, 복잡한 팩토리 패턴 대신에 자바 new 연산자를 사용하면서, 신참 사용자들도 객체를 쉽게 조작할 수 있게 해준다. JDOM을 사용하여 XML 문서를 구현하는 방법을 살펴보자. 우리가 구현할 구조는 Listing 1과 같다. (참고자료 섹션에서 전체 코드를 다운로드 할 수 있다.)


Listing 1. 구현할 XML 문서 샘플
                

<?xml version="1.0" encoding="UTF-8"?>
<car vin="123fhg5869705iop90">
<!--Description of a car-->
<make>Toyota</make>
<model>Celica</model>
<year>1997</year>
<color>green</color>
<license state="CA">1ABC234</license>
</car>

주: 아래 Listing 2부터 7까지 샘플 문서를 구현할 것이다.

먼저, 루트(root) 엘리먼트를 만들고 이를 문서에 추가한다.


Listing 2. Document 구현하기
                

Element carElement = new Element("car");
Document myDocument = new Document(carElement);

이 단계는 새로운 org.jdom.Element를 만들고, 이것을 org.jdom.Document myDocument의 루트 엘리먼트로 만든다. (참고자료 섹션에서 제공하는 샘플 코드를 사용하고 있다면 반드시 org.jdom.*을 반입하라.) XML 문서는 하나의 루트 엘리먼트를 가져야 하므로, Document는 생성자에 Element를 취한다.

다음에는, vin 애트리뷰트를 추가한다.


Listing 3. Attribute 추가하기
                

carElement.addAttribute(new Attribute("vin", "123fhg5869705iop90"));

엘리먼트를 추가하는 것도 매우 단순하다. make 엘리먼트를 추가한다.


Listing 4. 엘리먼트와 하위 엘리먼트
                

Element make = new Element("make");
make.addContent("Toyota");
carElement.addContent(make);

ElementaddContent 메소드가 Element를 리턴하므로, 이를 다음과 같이 작성할 수 있다.


Listing 5. 간결한 형식으로 엘리먼트 추가하기
                

carElement.addContent(new Element("make").addContent("Toyota"));

이 문장들 모두 같은 일을 수행한다. 첫 번째 예제는 보다 읽기 쉽지만, 두 번째는 많은 엘리먼트들을 한번에 구현한다면 더욱 읽기 쉬울 것이라고 말할 수도 있겠다. 문서 구현을 완료하려면 다음과 같이 한다.


Listing 6. 나머지 엘리먼트 추가하기
                

carElement.addContent(new Element("model").addContent("Celica"));
carElement.addContent(new Element("year").addContent("1997"));
carElement.addContent(new Element("color").addContent("green"));
carElement.addContent(new Element("license")
.addContent("1ABC234").addAttribute("state", "CA"));

license 엘리먼트의 경우, 엘리먼트의 콘텐트를 추가했을 뿐만 아니라, 여기에 애트리뷰트도 추가하면서, 라이센스가 발행되었던 상태를 지정하고 있다. Element에 대한 addContent 메소드는 Element만 리턴하기 때문에 이것이 가능하다.

주석 섹션이나 기타 표준 XML 유형을 추가하는 것도 같은 방식이다.


Listing 7. 주석 추가하기
                

carElement.addContent(new Comment("Description of a car"));

문서 조작은 비슷한 방식으로 발생한다. 예를 들어, year 엘리먼트에 대한 레퍼런스를 획득하려면, ElementgetChild 메소드를 사용한다.


Listing 8. 자식 엘리먼트에 액세스 하기
                

Element yearElement = carElement.getChild("year");

이 문은 실제로 엘리먼트 이름 year를 가진 자식 Element를 리턴한다. year 엘리먼트가 없다면, 호출은 어떤 것도 리턴하지 않는다. DOM Node Node 인터페이스 같은 것에서 리턴 값을 던질 필요가 없었다. Element의 자식 들은 단순히 Element이다. 비슷한 방식으로, 문서에서 year 엘리먼트를 제거할 수 있다.


Listing 9. 자식 엘리먼트 제거하기
                

boolean removed = carElement.removeChild("year");

이 호출은 year 엘리먼트만 제거한다. 나머지 문서는 바뀌지 않은 채로 남아있다.

엘리먼트만 제거한다. 나머지 문서는 바뀌지 않은 채로 남아있다. XMLOutputter 클래스를 사용한다.


Listing 10. JDOM을 XML 텍스트로 바꾸기
                

try {
XMLOutputter outputter = new XMLOutputter(" ", true);
outputter.output(myDocument, System.out);
} catch (java.io.IOException e) {
e.printStackTrace();
}

XMLOutputter는 포맷팅 옵션을 갖고 있다. 여기에서, 우리는 부모 엘리먼트에서 두 스페이스를 들여쓰기 한 자식 엘리먼트를 원한다고 지정했고, 엘리먼트들 사이에 새로운 라인을 원한다는 것을 지정했다. XMLOutputterWriter 또는 OutputStream을 출력한다. 파일로 출력하려면 아웃풋 라인을 다음과 같이 바꾼다.


Listing 11. FileWriter를 사용하여 XML 출력하기
                

FileWriter writer = new FileWriter("/some/directory/myFile.xml");
outputter.output(myDocument, writer);
writer.close();




위로


기존 XML 툴과 결합하기

JDOM의 재미있는 기능들 중 하나는 다른 API들과의 상호 운용성이다. JDOM을 사용하여, Stream 또는 Reader 뿐만 아니라, SAX Event Stream 또는 DOM Document로 서도 문서를 출력할 수 있다. 이러한 유연성 때문에 JDOM이 이종의 환경에서 사용될 수 있고, XML을 핸들링 하는 또 다른 메소드를 이미 사용하고 있는 시스템에 추가될 수 있다. 나중에 예제에서 보겠지만, JDOM은 JDOM 데이터 구조를 인식하지 못하는 다른 XML 툴을 사용할 수 있다.

JDOM의 또 다른 사용법은 이미 존재하는 XML 데이터를 읽고 조작하는 기능이다. 잘 구성된 XML 파일을 읽는 것은 org.jdom.input의 클래스들 중 하나를 사용함으로써 수행된다. 이 예제에서, 우리는 SAXBuilder를 사용할 것이다.


Listing 12. SAXBuilder를 사용하여 XML 파일 파싱하기
                

try {
SAXBuilder builder = new SAXBuilder();
Document anotherDocument =
builder.build(new File("/some/directory/sample.xml"));
} catch(JDOMException e) {
e.printStackTrace();
} catch(NullPointerException e) {
e.printStackTrace();
}

Listing 2부터 7까지의 방식과 똑같이 이 프로세스를 통해 구현된 문서를 조작할 수 있다.

JDOM의 또 다른 적용은 이를 Apache의 Xalan 제품과 결합하는 것이다. (참고자료) 위 자동차 예제를 사용하여, 특정 자동차에 대한 상세를 제공하는 온라인 자동차 딜러용 웹 페이지를 구현할 것이다. 우선, 이 문서는 우리가 사용자에게 제공하고자 하는 자동차에 대한 정보를 나타낸다. 그런 다음, 이 JDOM Document를 XSL 스타일 시트로 결합하고, HTML 포맷의 결과를 서블릿의 OutputStream으로 출력할 수 있다.

이 경우, 우리가 사용할 XSL 스타일시트는 car.xsl이다.


Listing 13. 자동차 기록을 HTML로 변형하는 XSL 문서
                

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/car">
<html>
<head>
<title><xsl:value-of select="make"/> <xsl:value-of select="model"/>
</head>
<body>
<h1><xsl:value-of select="make"/></h1><br />
<h2><xsl:value-of select="model"/></h2><br />
<table border="0">
<tr><td>VIN:</td><td><xsl:value-of select="@vin"/></td></tr>
<tr><td>Year:</td><td><xsl:value-of select="year"/></td></tr>
<tr><td>Color:</td><td><xsl:value-of select="color"/></td></tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

이제, org.jdom.Document를 DOM Document로 바꾸고, 이것을 Xalan에 제공한다. XSL과 가상의 애플리케이션 서버에서 가져온 OutputStream을 나타내는 파일도 함께 제공한다. (Listing 14)


Listing 14. JDOM과 Xalan을 사용하여 HTML 문서 생성하기
                

TransformerFactory tFactory = TransformerFactory.newInstance();

// Make the input sources for the XML and XSLT documents
org.jdom.output.DOMOutputter outputter = new org.jdom.output.DOMOutputter();
org.w3c.dom.Document domDocument = outputter.output(myDocument);
javax.xml.transform.Source xmlSource =
new javax.xml.transform.dom.DOMSource(domDocument);
StreamSource xsltSource =
new StreamSource(new FileInputStream("/some/directory/car.xsl"));

// Make the output result for the finished document using
// the HTTPResponse OutputStream
StreamResult xmlResult = new StreamResult(response.getOutputStream());

// Get a XSLT transformer
Transformer transformer = tFactory.newTransformer(xsltSource);

// Do the transform
transformer.transform(xmlSource, xmlResult);

이 예제에서, 아웃풋은 자바 서블릿의 HTTPResponse OutputStream을 통해 체계화 된다. 하지만, 이 스트림은 XMLOutputter를 사용한 이전 예제처럼 쉽게 파일스트림이 될 수 있다. 우리는 DOMOutputter를 사용하여 Xalan용 XML 소스를 생성했다. 하지만, XMLOutputter를 사용하여 XML 문서를 String으로서 출력하고 이를 StreamSource로 만듦으로써 같은 인풋을 생성할 수 있었다. 유연성에 대해서 보자. JDOM은 그 구조를 String, SAX Event Stream, 또는 DOM Document로서 출력할 수 있다. 이는 JDOM이 인풋으로서 이러한 모델을 취할 수 있는 툴과 상호 작동할 수 있도록 해준다. (JDOM 웹 사이트에서 contrib 패키지를 참조하라. 여기에서 JDBC ResultSet-기반 빌더, XPATH 구현 같은 툴을 제공하는 JDOM 기반 유틸리티의 라이브러리를 찾을 수 있다.)

몇 줄의 코드로, JDOM은 다양한 기능을 실행한다. 우리는 XML에서 파싱하고 프로그래밍 방식으로 XML 문서를 생성하고, 그러한 문서들을 조작했고, XML 중심의 웹 페이지를 생성하는데 이를 사용했다.

Sun과 JDOM
JDOM 의 공식 1.0 릴리스는 Java Community Process의 진행 과정과 발을 맞춘다. JSR-102로서 제출된 JDOM은 핵심 자바 플랫폼에 들어가도록 승인을 얻었다. 다음은 Sun 측에서 말한 부분이다. "JDOM은 이전 API들보다 사용하기가 훨씬 더 쉽기 때문에, 이 플랫폼에 매우 유용할 것이라고 생각한다." JSR에 따르면, 1.0 릴리스는 JDOM의 패키징 변화를 "org.jdom"에서 "javax.xml.tree"로 간주하고 있다. 미래는 긍정적이지만, 개발자들은 새로운 버전에 발을 맞추려면 코드를 개선해야 한다.



위로


JDOM의 성장: 미래

이 글을 쓰고 있는 현재, JDOM 프로젝트는 Beta 6 버전을 릴리스 했다. 베타 상태임에도, JDOM은 안정적인 구현으로 입증되었다. API의 많은 부분이 안정적이고, 기존 인터페이스들에 잠재적으로 영향을 줄 많은 부분에서 작업이 진행 중이다. 따라서, 이 시점에서 진행되는 어떤 개발 프로젝트라도 JDOM을 무시해서는 안되겠다. 특정 메소드 시그너처와 특정 의미가 바뀔 것이고 핵심 자바 API에 채택될 것이기 때문이다. (Sun과 JDOM 참조)

JDOM을 위한 단기적인 TO-DO 리스트는 API를 안정화 하고 성능 부분을 평가하는 것에 초점이 맞춰졌다. 개발자들을 애먹이는 부분에는 DTD 엔터티 지원과 기타 구조들이다. XPATH 지원과 보다 직접적인 XML 데이터 소스와의 통합 등이 진행 중이다.

그래서, JDOM은 기존 XML API들보다 더 나은가? 여러분이 자바로 꿈을 꾼다면 대답은 '그렇다'이다. JDOM이 여러분이 선호하는 파서나 XML 인식 데이터베이스를 대체하는 것은 아니지만, 이것의 디자인 원리는 XML 세계에 적용될 수 있다.



참고자료



필자소개


Wes Biggs는 Los Angeles Times, USWeb, Elite Information Systems 등 여러 기업들에서 인터넷 애플리케이션을 개발했다. 오픈 소스 자바 프로젝트의 기여자이며, Free Software Foundation의 gnu.regexp 정규식 패키지를 관리하고 있다. (wes@tralfamadore.com)



Harry Evans는 소프트웨어 디자인과 애플리케이션 엔지니어링 분야에서 경력을 쌓았다. 여러 웹 기반 및 인터넷 인식 제품들의 디자인 분야에서 일했다. Rapid Application Development부터 레거시 제품 통합까지 경력을 쌓았다. (harry@tralfamadore.com)

Returning a JDBC result set from an Oracle stored procedure


Returning a JDBC result set from an Oracle stored procedure

Bruce P. Blackshaw

Introduction

You would think that returning a JDBC result set from an Oracle PL/SQL stored procedure would be quite straightforward. Unfortunately, it's not as easy as it sounds. But reading this article should tell you all you need to know. Any contributions or corrections welcome - please email me.

There are three basic steps. First, you have to write the stored procedure - which in Oracle is a bit tricky if you're not familiar with PL/SQL. Testing it using the Oracle command-line tool sqlplus is also quirky. And calling it via JDBC isn't exactly standard either.

Writing the stored procedure

If you are used to Sybase or SQLServer, returning a result set from a stored procedure is easy. Just finish the procedure with a "select x,y,z from my_table", selecting whatever columns you wish to return.

Not so for Oracle. You need to use cursors.

A cursor is a work area for a SQL statement that returns one or more rows. It allows you to fetch rows from the result set one by one. Cursors aren't particularly difficult to use, but to return a result set from a PL/SQL stored procedure, you must use a cursor variable. Cursor variables are basically pointers to cursors, and you use them to pass around references to cursors, such as a parameter to a stored procedure. The PL/SQL type of a cursor variable is REF CURSOR.

To use cursor variables, you must define a REF CURSOR type. This type can be strong or weak. Strong types are tied to a specific set of a table's columns. This means a strong REF CURSOR type can only be used with queries (i.e. cursors) returning those columns. Weak types can refer to any cursor, and so are much more flexible. They do, however, sacrifice type safety.

The easiest way forward is to define a weakly typed REF CURSOR in a PL/SQL package. Packages are used in PL/SQL for partitioning functionality. Below, a weak REF CURSOR type called REF_CURSOR is defined in a PL/SQL package called types.

CREATE OR REPLACE PACKAGE types
AS
    TYPE ref_cursor IS REF CURSOR;
END;

This definition can now be used in all stored procedures to declare a variable of type REF CURSOR.

We can now write a stored procedure that returns a REF CURSOR, that in JDBC we can process as a result set.

Assume we start from a table defined as below.

CREATE TABLE STOCK_PRICES(
    RIC VARCHAR(6) PRIMARY KEY,
    PRICE NUMBER(7,2),
    UPDATED DATE )

Here we have a table of stock prices, with the RIC (Reuters Instrument Code) as the primary key.  We define a PL/SQL function that simply declares a cursor that returns all columns for stocks below a certain price.

CREATE OR REPLACE FUNCTION sp_get_stocks(v_price IN NUMBER)
    RETURN types.ref_cursor
AS
    stock_cursor types.ref_cursor;
BEGIN
    OPEN stock_cursor FOR
        SELECT ric,price,updated FROM stock_prices
        WHERE price < v_price;

    RETURN stock_cursor;
END;

These can all be created via the sqlplus command-line tool. Download the SQL script, and start up sqlplus.

C:\>sqlplus username/password

Once in sqlplus, run the script. If you start up sqlplus in the directory the SQL script is in, you can run it using the @ command:

SQL>@stock_prices

You should get an encouraging list of messages such as "1 row created", "Package created", "Function created". If instead you get error messages such as "insufficient privileges", your user must be given additional database privileges. The predefined Oracle roles CONNECT and RESOURCE should be what you need. Login as the system user and run the below:

SQL>GRANT CONNECT, RESOURCE TO username;

Testing the stored procedure

To test the stored procedure, we can either execute it from within an environment that allows you to execute stored procedures and supply their parameters, such as SQL Navigator or Toad, or we can run it from within sqlplus.

Executing a stored procedure from within sqlplus is straightforward once you know how.

Firstly, start up sqlplus and declare a sqlplus variable of type refcursor. This declares a sqlplus variable of type refcursor (a sqlplus type), called "results":

SQL> var results refcursor

Next, execute the procedure, supplying a number as a parameter, and assigning the result into our variable, "results". Note the unusual syntax.

SQL> exec :results := sp_get_stocks(20.0)

PL/SQL procedure successfully completed.

Finally, use the sqlplus print statement to print out the result set

SQL> print results

RIC PRICE UPDATED
------ --------- ---------
AMZN 15.5 21-OCT-01
SUNW 16.25 21-OCT-01
ORCL 14.5 21-OCT-01

If this works successfully, similar to the above, the stored procedure (well, function) is working correctly.

Calling the stored procedure from Java

There are some Oracle-specific tricks to calling the above stored procedure from Java.

The query string can be in the Oracle format or the standard JDBC callable statement syntax. If you are using the Oracle syntax, the query string is:

begin ? := sp_get_stocks(?); end;

If you are using the JDBC syntax, the query string is:

{ call ? := sp_get_stocks(?) }

In each case the spacing is important - the query will not work otherwise.

Secondly, the out parameter must be registered to be of type OracleTypes.CURSOR.

And finally, use getObject() to retrieve the result set.

The code (without error handling) is illustrated below. A more complete code sample can be downloaded from here.

String query = "begin ? := sp_get_stocks(?); end;";

CallableStatement stmt = conn.prepareCall(query);

// register the type of the out param - an Oracle specific type
stmt.registerOutParameter(1, OracleTypes.CURSOR);

// set the in param
stmt.setFloat(2, price);

// execute and retrieve the result set
stmt.execute();
ResultSet rs = (ResultSet)stmt.getObject(1);

// print the results
while (rs.next()) {
    System.out.println(rs.getString(1) + "\t" +
        rs.getFloat(2) + "\t" +
        rs.getDate(3).toString());
}

Hopefully, this code will be all you need to get started on calling your own Oracle stored procedures via JDBC.

References

  1. Oracle JDBC FAQ
  2. Oracle JDBC How-To Documents

참조 :
http://www.idevelopment.info/data/Programming/java/jdbc/PLSQL_and_JDBC/RefCursorExample.java

DBMS_OUTPUT.PUT_LINE

DBMS_OUTPUT.PUT_LINE allows you to write information to a buffer throughout the execution of a trigger/procedure. That information is available to be read by a trigger/procedure (using GET_LINE(S)), or dumped to SQL*Plus upon completion of execution.

One of the most common misconceptions is that PUT_LINE writes data immediately to SQL*Plus. That is not true. PUT_LINE only puts it in the buffer. You will not see it before the block has executed. I can prove that with this example (note: you must load the user_lock package for this):

scott@Robert> BEGIN
2 DBMS_OUTPUT.PUT_LINE('Going to sleep for 10 seconds...');
3 USER_LOCK.SLEEP(1000);
4 DBMS_OUTPUT.PUT_LINE('Woke up after 10 seconds.');
5 END;
6 /
Going to sleep for 10 seconds...
Woke up after 10 seconds.

You will have seen both messages come out after 10 seconds as opposed to one before and one after.

Despite the fact that it doesn't write messages throughout its progress, PUT_LINE can still make a useful debugging tool. I like the way that the messages can be kept but easily disabled by using DBMS_OUTPUT.DISABLE. Any PUT_LINE messages are silently ignored if you have DISABLEd DBMS_OUTPUT (or failed to ENABLE).

To see the messages, you need to call DBMS_OUTPUT.ENABLE. The only parameter is buffer_size, which, if NULL, will default to 20000. The buffer size can be anywhere from 2000 to 1000000.

scott@Robert> BEGIN
2 DBMS_OUTPUT.DISABLE;
3 DBMS_OUTPUT.PUT_LINE('Disabled');
4 DBMS_OUTPUT.ENABLE;
5 DBMS_OUTPUT.PUT_LINE('Enabled');
6 END;
7 /
Enabled

PL/SQL procedure successfully completed.

Incidentally, SQL*Plus's SET SERVEROUTPUT ON will call DBMS_OUTPUT.ENABLE. You can even use SIZE with that command. SET SERVEROUTPUT also includes formatting options, such as FORMAT WRAPPED, WORD_WRAPPED and TRUNCATE (along with a SET LINESIZE) to get the output the way you want it. [EDIT: Fixed Typo]

There are two common errors related to DBMS_OUTPUT.PUT_LINE. The first one is trying to put more than 255 characters per line.

scott@Robert> DECLARE
2 l_string VARCHAR2(300);
3 BEGIN
4 l_string := '1234567890';
5 l_string := l_string || l_string || l_string || l_string || l_string;
6 l_string := l_string || l_string || l_string || l_string || l_string || l_string;
7 DBMS_OUTPUT.PUT_LINE(l_string);
8 END;
9 /
DECLARE
*
ERROR at line 1:
ORA-20000: ORU-10028: line length overflow, limit of 255 chars per line
ORA-06512: at "SYS.DBMS_OUTPUT", line 35
ORA-06512: at "SYS.DBMS_OUTPUT", line 133
ORA-06512: at line 7

The solution here is to use DBMS_OUTPUT.NEW_LINE to split it up into lines. 255 is a hard limit, if you really want to print a line with more than that, you can write your own package that does the same thing as DBMS_OUTPUT. That is actually a very common thing to do. Tom Kyte's has a handy one in Appendix A of "Expert One-on-One Oracle."

The second common error is overfilling your buffer.

scott@Robert> BEGIN
2 DBMS_OUTPUT.ENABLE(2000);
3 FOR i IN 1..1000 LOOP
4 DBMS_OUTPUT.PUT_LINE('This is line ' || i);
5 END LOOP;
6 END;
7 /
This is line 1

This is line 105
BEGIN
*
ERROR at line 1:
ORA-20000: ORU-10027: buffer overflow, limit of 2000 bytes
ORA-06512: at "SYS.DBMS_OUTPUT", line 35
ORA-06512: at "SYS.DBMS_OUTPUT", line 198
ORA-06512: at "SYS.DBMS_OUTPUT", line 139
ORA-06512: at line 4

The solution here is increase the size of your buffer, using ENABLE. The maximum size is 1000000 and that is a hard limit. Once again, you can write your own package as a workaround.

This example also illustrated that even if you have an exception, the contents of the buffer until that point is still available.

The alternative to writing your own package is to write your messages to a table. Then you can query the table at any time to see your debug messages. DBMS_PIPE is another option to consider.

I will close with two more interesting "gotchas" for DBMS_OUTPUT.PUT_LINE.

scott@Robert> BEGIN
2 DBMS_OUTPUT.PUT_LINE(' What happened to my leading spaces?');
3 END;
4 /
What happened to my leading spaces?


This is an SQL*Plus Gotcha. Just be sure to use FORMAT WRAPPED, like so:

scott@Robert> SET SERVEROUTPUT ON FORMAT WRAPPED
scott@Robert> BEGIN
2 DBMS_OUTPUT.PUT_LINE(' There they are!');
3 END;
4 /
There they are!


Here is the second gotcha.

scott@Robert> DECLARE
2 l_bool BOOLEAN;
3 BEGIN
4 l_bool := TRUE;
5 DBMS_OUTPUT.PUT_LINE(l_bool);
6 END;
7 /
DBMS_OUTPUT.PUT_LINE(l_bool);
*
ERROR at line 5:
ORA-06550: line 5, column 1:
PLS-00306: wrong number or types of arguments in call to 'PUT_LINE'
ORA-06550: line 5, column 1:
PL/SQL: Statement ignored

DBMS_OUTPUT.PUT_LINE is not overloaded for Booleans. The solution is to either to write your own package (as mentioned above), or convert from Boolean type to something else for the PUT_LINE call.

For more information, this package is described in (among other places) Oracle's Supplied PL/SQL Packages Reference Guide, Chapter 43:
http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96612.pdf

And here is Dan Morgan's Quick Reference:
http://www.psoug.org/reference/dbms_output.html


===============================

오라클에서 중간에 결과값을 찍어볼 때 DBMS_OUTPUT.PUT_LINE() 구문을 사용한다.

(SQL*Plus에서) 결과가 나오지 않는 경우엔 긴장하지 말고,

SQL> SET SERVEROUTPUT ON

입력 이후 확인해 보도록한다.

--TABLE 만들기
--시퀀스 생성
CREATE SEQUENCE tt1_seq start with 1

/
--테이블 생성
CREATE TABLE tt1
(
 temp_id   NUMBER not null primary key,
 data1   VARCHAR2(50),
 data2   VARCHAR2(50),
 create_time TIMESTAMP
)
/
--트리거 생성
CREATE OR REPLACE TRIGGER tt1_ti
   before insert on tt1
   for each row
   declare
    l_temp_id number;
   begin
    select tt1_seq.nextval
      into l_temp_id
      from dual;
   :new.temp_id := l_temp_id;
   end;
/


-- 프로시저 패키지 생성

-- 프로시저 단독으로 사용가능 하지만 원활한 관리를 위해 패키지구성을 통해 활용한다.
CREATE OR REPLACE PACKAGE DATA_PROCESSES IS
 PROCEDURE insert_tt1(l_vdata1 IN VARCHAR2,
    l_vdata2  IN VARCHAR2);
 PROCEDURE delete_tt1(l_nid  IN NUMBER);
 PROCEDURE update_tt1(l_nid  IN NUMBER, l_vdata1 IN VARCHAR2,  l_vdata2  IN VARCHAR2);
END DATA_PROCESSES;
/
-- 프로시저 바디 생성
CREATE OR REPLACE PACKAGE BODY DATA_PROCESSES AS
    -- 삽입 프로시저 생성
     PROCEDURE insert_tt1(l_vdata1 IN VARCHAR2, l_vdata2  IN VARCHAR2)    
     IS  l_stmt_str VARCHAR2(200);    
     BEGIN
        SAVEPOINT start_transaction;  ---- 세이브 포인트 지정
        l_stmt_str := 'INSERT INTO tt1 (data1,  data2, create_time) ' || 'VALUES(:l_vdata1, :l_vdata2, SYSDATE)';
        EXECUTE IMMEDIATE l_stmt_str   USING l_vdata1, l_vdata2;
    
     EXCEPTION
        WHEN OTHERS THEN
        ROLLBACK TO start_transaction; ---- 애러 발생시 애러구문 출력이후 롤백 시켜준다.
        dbms_output.put_line('An error occurred while creating query:' ||    SQLERRM ||'.  Please try again later.');
     END;
    
    -- 삭제 프로시저 생성
     PROCEDURE delete_tt1(l_nid  IN NUMBER)
     IS  l_stmt_str VARCHAR2(200);
     BEGIN
        SAVEPOINT start_transaction; ---- 세이브 포인트 지정
        l_stmt_str := 'DELETE FROM tt1 WHERE temp_id = :1';
        EXECUTE IMMEDIATE l_stmt_str USING l_nid;
     EXCEPTION
        WHEN OTHERS THEN
        ROLLBACK TO start_transaction; ---- 애러 발생시 애러구문 출력이후 롤백 시켜준다.
        dbms_output.put_line('An error occurred while creating query: '|| SQLERRM ||'.  Please try again later.');
     END;
    
    -- 업데이트 프로시저 생성
    /*
        변수 입력받는것과 USING 구문만 효력을 발생한다. sql문에서는 using문에 입력된 순서대로 변수값을 매칭 시킨다.
        l_stmt_str :=에서 :1 :2 :3 이나 :3 :1 :2나 같은 결과를 초래한다.
    */
    PROCEDURE update_tt1(l_nid  IN NUMBER, l_vdata1 IN VARCHAR2, l_vdata2  IN VARCHAR2)
    IS    l_stmt_str VARCHAR2(200);
    BEGIN
        SAVEPOINT start_transaction;  ---- 세이브 포인트 지정
        l_stmt_str := 'UPDATE tt1 SET data1=:3, data2=:1 WHERE temp_id = :2';
    EXECUTE IMMEDIATE l_stmt_str USING  l_vdata1, l_vdata2, l_nid ;
    
    EXCEPTION
    WHEN OTHERS THEN
    ROLLBACK TO start_transaction; ---- 애러 발생시 애러구문 출력이후 롤백 시켜준다.
        dbms_output.put_line('An error occurred while creating query: '|| SQLERRM || '.  Please try again later.');
    END;
END DATA_PROCESSES;


[TEST]
exec DATA_PROCESSES.insert_tt1('data_clm1','data_clm2');
exec DATA_PROCESSES.insert_tt1('data_clm3','data_clm4');
select * from tt1;
exec DATA_PROCESSES.delete_tt1(5);
select * from tt1;
exec DATA_PROCESSES.update_tt1(4,'data1111','data2222');
select * from tt1;


[추천유틸]
원할한 테스트를 위해서 DB관련 유틸을 사용해야 하는데... 개인적으로는 간단편하게 사용할 수 있는
Benthic Software에서 만든 Golden(디비 컨트롤유틸)과 PLEdit와 (프로시져 생성 전용) 추천한다.
음.. 쉐어웨어 인데.. google에서 검색하면 암흑버전 뭐 손쉽게 얻을 수 있고 -_-;;
무엇보다 토드, 오렌지 ... 과 비교하여 볼때 경량형 프로램인지라 좋은거 같음.


'etc > old' 카테고리의 다른 글

Returning a JDBC result set from an Oracle stored procedure  (0) 2008.05.30
DBMS_OUTPUT.PUT_LINE  (0) 2008.05.30
今月の運勢(うん‐せい)  (0) 2008.05.30
blazeDS 개발자 문서  (0) 2008.05.29
org.apache.catalina.core.AprLifecycleListener  (0) 2008.05.29
=================================================================================
今月の運勢(うん‐せい)
=================================================================================
習慣(しゅうかん)は重要(じゅうよう)な事だと思います。
昨日一日日記を書いていないのに、いざと書けなれば難しいです。
時間にかかわらず運動と同じように每日書くのが重要です。
わたしは何かを考えたときに爪(つめ)とか指(ゆび)を砕(くだ)きます。
先生のよい習慣と惡いのは何ですか。秘密(ひみつ)ですか。ふふふ。
今日は、書店(しょてん)で日本語の勉強のためにTV JAPANを買いました。
(実は日本のドラマに興味を持っていて) その中の12星座(せいざ)占(うらな)いによるば,
(わたしは水瓶(すいびょう)星座です。) 伸び伸び動(う)ごける好調期(こうちょうき)才能(さいのう)
才能(さいのう)は惜(お)しみなく披露(ひろう)を
好きな分野への探求心(たんきゅうしん)が功(こう)を奏(そう)し, ...以下省略(いかしょうりゃく)
決論(けつろん) : 金運(うん)UPグッズ★レンタルビデオ, 開運ナンバ-★1。ハ_ハ。
=================================================================================
知らない 單語
=================================================================================
習慣(しゅうかん) 습관. 풍습.
いざ (부사) 막상
かかわらず 「…に(も)~」의 꼴로 …에 관계없이. …에 불구하고. …인데도.
爪(つめ) 손톱
砕(くだ)く 깨물다
とか [병렬조사] 〈「…~…~」의 꼴로〉 …라든가. …든지. …거니.
占(うらな)い【占い】점. 점을 침. 점쟁이.
星座(せいざ) 성좌. 별자리.
水瓶(すいびょう) 물병
伸び伸び(のびのび) 거침없이 자유롭게 자라는 모양. 구김살없이. 무럭무럭. 쭉쭉.
うご・く【動く】움직이다. 이동하다.
好調(こうちょう) 호조. 순조로움
期(き) 때. 기간.
才能(さいのう) 재능
惜(お)しみ 아낌. 아쉬워함.
披露(ひろう) 피로
功(こう) 공적
奏(そう)する 인상(人相)·수상(手相)·가상(家相) 등을 보아 그 좋고 나쁨, 길흉 등을 판단한다.
省略(しょうりゃく) 생략
金運(きんうん) 돈에 관한 운세
開運(かいうん) 개운. 운이 트임.
=================================================================================
사용자 삽입 이미지



시간되면 봐야지 ㅡ,.ㅡ;;

출처 : Adobe 홈페이지
Dec 5, 2006 6:54:28 PM org.apache.catalina.core.AprLifecycleListener lifecycleEvent
INFO: The Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: ....

메시지가 뜬다면 .... tcnative-1.dll 파일이 서버에 존재하지 않아서 그런것임.

http://tomcat.heanet.ie/native/1.1.12/binaries/win32/
(2008.05.29일 현재 최신버전)

파일을 다운받아 적당한 곳(Path설정된 곳)에 위치 시키면 됩니다.

======================================================================================
아래글은 참조 블로그 입니다.
======================================================================================
톰켓 5.5.20을 사용해서 개발을 진행하던중...

Dec 5, 2006 6:54:28 PM org.apache.catalina.core.AprLifecycleListener lifecycleEvent
INFO: The Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: ....

톰켓 시작시 항상 나오는 찜찜한 정보...
제대로 동작은 하는데 뭔지 모를 찜찜함... ㅡㅡ;;

Dec 29, 2006 1:15:53 PM org.apache.catalina.core.AprLifecycleListener lifecycleEvent
INFO: Failed shutdown of Apache Portable Runtime

이눔이 종료할때두 항상 나오는것이 아닌가...?? ㅡㅡ^

그냥 무시하고 살았는데
저게 도대체 왜 뜨는건지 궁금증이 밀려와서 디벼보기 시작했다.

시작시 정보는... 뭔가를 찾지 못한다..
종료시엔 내가 엄청 싫어하는... Fail.... 어쩌구 저쩌구.. @.@


그래서~ 소스를 디컴파일 하면서 쭈~~~~~~~~~~~~~~~~~~ㄱ 찾아가 보니...

JNI (Java Native Interface) 관련된 내용을 못찾는거던데...

"tcnative-1", "libtcnative-1"

요 두 파일을 시스템의 PATH에서 찾아보니 없댄다.

파일 검색을 해보니 진짜루 없는게 아닌가..? @.@

이상하다.... 해서 구글링을 해보니...

저넘을 따로 갖다놔야 된댄다. ㅡㅡ;;

http://tomcat.heanet.ie/native/1.1.6/binaries/

요기에서 내 로컬 개발서버인 윈도우즈에 맞는 DLL을 적당한 경로에
(난 JDK bin 디렉토리에 뒀다.) 복사하고

로컬 개발서버에선 깔끔하게 해결~!! ㅋㅋㅋ


개발서버인 리눅스엔 바이너리 복사 시도 실패. ㅡㅡ^

rpm 으로 설치 실패. ㅡㅡ++

소스 컴파일.. 성공.

하고 톰캣 실행 스크립트에 LD_LIBRARY_PATH에다가

경로 지정해줬더니 에러(?)가 안뜬다... ㅋㅋㅋ

짜슥들 좀 기본적으로 포함시켜두면 어디가 덧나나?? ㅡㅡ^

아무튼...

이제 깔끔하게 일 시작해 볼까~


그나저나...

리눅스쪽에 소스 설치하다가 알았는데...

APR (Apache Portable Runtime) 이넘... (http://apr.apache.org)

뭐하는 넘인지 나중에 좀 찾아봐야겠다.

소스 설치할때 이넘땜에 고생좀 했다.. ㅡㅡ;;

* 객체직렬화의 개념

 

자바 I/O 처리는 정수, 문자열, 바이트 단위의 처리만 지원했었다.

따라서 복잡한 객체의 내용을 저장/복원하거나, 네트워크로 전송하기 위해서는 객체의 멤버변수의

각 내용을 일정한 형식으로 만들어(이것을 패킷이라고 한다) 전송해야 했다.


객체직렬화는 객체의 내용(구체적으로는 멤버변수의 내용)을 자바 I/O가 자동적으로 바이트 단위로 변환하여, 저장/복원하거나 네트워크로 전송할 수 있도록 기능을 제공해준다.

즉, 개발자 입장에서는 객체가 아무리 복잡하더라도, 객체직렬화를 이용하면 객체의 내용을 자바 I/O가 자동으로 바이트 단위로 변환하여 저장이나 전송을 해주게 된다.

또한 이것은 자바에서 자동으로 처리해주는 것이기 때문에, 운영체제가 달라도 전혀 문제되지 않는다.

 

객체를 직렬화할때 객체의 멤버변수가 다른 객체(Serializable 인터페이스를 구현한)의 레퍼런스 변수인 경우에는 레퍼런스 변수가 가리키는 해당 객체까지도 같이 객체직렬화를 해버린다.

또 그 객체가 다른 객체를 다시 가리키고 있다면, 같은 식으로 객체직렬화가 계속해서 일어나게 된다.

이것은 마치 객체직렬화를 처음 시작한 객체를 중심으로 트리 구조의 객체직렬화가 연속적으로 일어나는 것이다.

 

 

* 도입이유

 

1. RMI의 도입

RMI는 원격객체통신을 지원해야 하기 때문에, 객체의 내용이 투명하게 이동할 수 있어야 한다

 

2. Beans

Beans는 설계시에 상태정보를 지정할 수 있는데, 이것을 마땅히 저장할 메커니즘이 없다.

이때 객체직렬화를 사용하면, 편하게 객체의 상태정보를 저장하는 것이 가능하다

 

3. 간단한 네트워크 프로그램이나 파일 프로그램은 객체직렬화를 사용하면, 코딩이 반으로 준다.

 

 

 

* 객체 직렬화의 과정


객체는 ObjectOutputStream의 writeObject() 메쏘드에 자신을 넘김으로써 직렬화 된다.
writeObject()메쏘드는 private 필드와 super 클래스로부터 상속받은 필드를 포함,
객체의 모든 것을 기록하게된다.
 
직렬화 해제는 직렬화와 반대의 과정을 거치게 되는데
ObjectInputStream의 readObject() 메서드를 호출함으로써
스트림으로부터 읽어 들이고 이를 직렬화 되기전의 객체로 다시 만들게 된다.

직렬화에 대한 간단한 예제이다.

ObjectOutputStream을 생성해서 writeObject() 메서드를 이용해서 객체를 직렬화하고,
ObjectInputStream을 생성해서 readObject() 메서드를 통해서 객체를 복원한다.
또한 SerializableClass가 Serializable을 implements한 것을 주의해서 보아야 한다.


----------------------------------------------------------------------------------

ObjectSerializableTest.java



import java.io.*;


public class ObjectSerializeTest {

public static void main(String[] args) throws Exception {


// 파일을 열어서 그곳에 객체를 직렬화시켜서 저장한다.
// 파일 출력스트림을 연다.
FileOutputStream fileout = new FileOutputStream("test.txt");


// 객체스트림을 열고, 객체스트림을 통해 객체를 파일에 저장
ObjectOutputStream out = new ObjectOutputStream(fileout);
out.writeObject(new SerializableClass("Serialize Test Program", 1014));


// 객체스트림을 닫는다.
out.close();


// 직렬화 된 객체를 저장된 파일로 부터 객체를 해제시켜 원래의 객체로 복원
// 파일 입력스트림으로부터 객체 입력스트림을 연다.
FileInputStream fileinput = new FileInputStream("test.txt");
ObjectInputStream in = new ObjectInputStream(fileinput);


// 객체 입력스트림으로부터 객체를 읽어온다.
SerializableClass sc = (SerializableClass)in.readObject();


// 객체스트림을 닫는다.
in.close();


// 스트림으로부터 읽어들인 객체의 내용을 출력
// 원래 생성되었던 객체와 같은 값을 갖는다는 것을 알수가 있다.
System.out.println("String : " + sc.Sstr);
System.out.println("Integer : " + sc.Sint);
}
}


// 하나의 문자열과 정수를 저장하고 있는 SerializableClass를
// Serializable을 implements 함으로써
// 스트림을 통해 직렬화되고 해제되어질 수 있다.

class SerializableClass implements Serializable {

public String Sstr;
public int Sint;


// 생성자
public SerializableClass(String s, int i) {
this.Sstr = s;
this.Sint = i;
}
}



-----------------------------------------------------------------
실행결과


String : Serialize Test Program
Integer : 1014





========================================================================================

자바는 객체지향언어이다.

즉, 가상머신 내에 존재하는 것은 모두 객체들로 이루어져 있습니다.

물론, 객체를 만들기 위한 클래스들도 있겠죠.

클래스의 형태를 보고 필요하다면 언제든지 객체를 만들어 낼 수 있습니다.

객체와 클래스의 관계를 논하지 않고서는 아무것도 할 수 없겠죠.

컴파일 한 후, 생성된 .class파일은 클래스의 모든 정보를 담고 있으며,

이 .class의 바이트는 가상머신에 Class클래스의 형태로 로딩 되어 집니다.

그리고, 로딩된 클래스의 정보를 보고 객체를 만들게 됩니다.

클래스는 데이터 타입이며 이 데이터타입이 있어야 그 모양을 보고 객체의 메모리를 생성할 수 있습니다.

 

특정 클래스의 객체를 만들었을 때 객체는 무엇으로 이루어져 있을까요?

객체는 멤버변수의 메모리의 크기와 같다.

객체는 멤버변수의 메모리만으로 이루어져 있습니다.

만약, 객체를 이용해서 메서드를 호출한다면 객체가 보유하고 있는 값과 호출할 메서드의 형태만 있으면 언제든지 호출 가능합니다.

메서드의 형태는 클래스의 정보가 있는 부분에 있을 것이고,

메서드 내에 멤버변수가 사용되어진다면, 그 값은 객체 내에 있으니 이 두 가지를 조합한다면 언제든지 메서드를 호출할 수 있습니다.

 

가상머신에 존재하는 객체메모리 그 자체를 저장하거나, 통째로 네트웍으로 전송하려고 합니다.

저장을 하든 네트웍으로 전송을 하든간에 객체는 일련의 바이트의 형태로 되어 있어야 합니다.

어떤 규칙에 의해서 객체 메모리를 한 줄로 늘어선 바이트의 형태로 만들고,

다시 객체의 형태로 복원하는 작업을 우리는 객체 직렬화라고 합니다

 

 

‘이름’, ‘부서’, ‘직책’ 이라는 속성을 가진 직원 클래스가 있고, 이 클래스를 이용하여 두 개의 객체 (직원1 객체와 직원2 객체)가 생성되어 메모리에 저장되어 있다면,

직원1 객체는 이름이 홍길동이고 부서는 총무부, 직책은 과장이라는 상태 정보를 저장하고 있습니다.

 

이러한 객체들이 저장 되어 있는 메모리는 휘발성이기 때문에,

컴퓨터의 전원을 종료하게 되면 객체의 상태 정보는 모두 사라집니다.

그래서 우리는 이 정보를 데이터베이스에 저장하거나 아니면 따로 기록해 두는 것입니다.

다시 객체로 만들려면 데이터베이스 내용을 검색해서 해당 내용을 찾아와서 객체의 형태로 다시 조합해야 합니다.

 

이러한 방법을 사용하는 대신, 객체 그 자체를 바로 저장하고 다시 불러왔을 때 원래의 객체 형태 그 자체라면 아주 효율적이겠죠.

 

정민철,영업부,부장 이라는 정보를 묶어서 소켓으로 전송한다면 다음과 같이 전송할 겁니다.

 

정민철|영업부|부장

 

이러한 방법으로 보내면 받는쪽에서는 “|”을 구분자로 해서 하나씩 분해해야 합니다.

만약 객체자체를 보낸다면 상황은 다릅니다. 다음과 같은 객체를 보낸다면

 

--------------------------------------------------------------

Employee.java

 

 

public class Employee {

   private String name;             // 이름

   private String dept;               // 부서

   private String duties;            // 직책

 

   public Employee (String name, String dept, String duties) {

       this.name = name;

       this.dept = dept;

       this.duties = duties;

   }// 생성자

 

public static void main(String[] args){

       Employee Emp = new Employee("조한서", "인사","차장");

     //Emp 자체를 네트웍으로 전송

   }

}

 

Emp 객체를 네트웍을 통해서 받았다면 바로 객체를 사용할 수 있다는 장점이 있습니다.

파싱할 필요도 없고, 특별한 작업 없이도 객체를 사용할 수 있는 방법론을 제공하는 것이 바로 객체 직렬화입니다.

이 개념이 RMI, Java Beans등의 핵심 기술이 됩니다.

 

객체 직렬화는 상당히 복잡한 과정을 필요로 하지만, 내부적으로 완벽하게 감추어져 있기 때문에 객체 직렬화를 직접 구현을 하는 것이 아니라 규칙에 맞게 사용하는 방법을 배우는 것이라고 보면 됩니다.

 

객체 직렬화에 대해서 정리하면

 

객체 직렬화는 객체의 상태를 보존하는 방법론을 제공한다.

파일 스트림, 네트워크 스트림 등과 함께 사용할 수 있는 확장성을 제공한다.

 

실제로 객체 직렬화 기술은 원격메소드호출(RMI : Remote Method Invocation)과 같은 기술에서 중요하게 사용이 됩니다.

RMI는 한쪽의 자바 가상 머신 내에 있는 객체가 멀리 떨어져 있는 원격지의 자바 가상 머신 내 객체를 네트워크를 통해 접근하고,

메서드를 호출 할 수 있게 해주는 기술입니다.

두 자바 가상 머신 사이에 연결된 바이트 스트림을 통해 메서드의 인자나 반환 값으로 객체를 주고 받기 위해서 객체 직렬화 기술이 사용됩니다.

또한, EJB라는 기술에서도 EJB 컨테이너의 성능 향상을 위해 사용됩니다.

 

객체 스트림에 저장될 객체는 Serializable이나 Externalizable 인터페이스를 구현 함으로써, 객체 자신이 저장될 의사가 있음을 반드시 밝혀야 합니다.

 

Serializable인터페이스는 아무 메서드도 가지고 있지않은 표시(태그) 인터페이스입니다. Serializable인터페이스를 구현한 객체는 객체 스트림 클래스들이 자동으로 필드들의 값을 저장하고 복구 해주지만,

Externalizable인터페이스를 구현한 객체는 객체를 표현할 필드들의 종류와 값을 writeExternal()메서드와 readExternal()메서드를 사용해서 직접 저장하고 복원하는 과정을 구현해야 합니다. 

 

 

* interface Serializable

 

객체직렬화가 필요한 객체는 반드시 Serializable 인터페이스르 구현해야 한다.

그러나, Serializable 인터페이스는 객체가 직렬화가 제공되어야 함을 자바가상머신(JVM)에 알려주는 역할만을 하는 인터페이스다.

따라서, Serializable 인터페이스를 지정하였다고 해도, 구현할 메서드는 없다.

 

보낼 객체가 직렬화 되어 있으면 전송은 특정 장치에 연결되어 있는 스트림이 모두 해결합니다.

우선 파일이나 네트웍에 스트림을 생성 한 후 객체를 보낼 수 있는 스트림으로 변환을 합니다.

그리고 직렬화되어 있는 객체를 보내면 됩니다.

그 순서를 정리하면 다음과 같습니다.

 

1. 네트웍이나 파일등에 스트림을 생성한다.

2. 생성된 스트림을 Object스트림으로 변환한다.

3. 입력과 출력스트림은 ObjectInputStream과 ObjectOutputStream이다.

4. 직렬화된 객체를 객체스트림을 통해서 전송한다.

    - ObjectOutputStream à writeObject(직렬화된객체)

5. 객체 스트림을 통해서 직렬화된 객체를 받는다.

    - ObjectInputStream à readObject()

 

이와 같은 순서로 객체 직렬화를 구현합니다.

스트림은 I/O에서 제공해 주기 때문에 보낼 객체만 생각하면 됩니다.

보낼 객체에 impelements Serializable만 붙이면 됩니다.

 

-------------------------------------------------------------

SerializableObject.java

 

 

import java.io.*;

 

public class SerialObject implements Serializable{

   private String name;        // 이름

   private String dept;          // 부서

   private String duties;       // 직책

   public SerialObject (String name, String dept, String duties) {

       this.name = name;

       this.dept = dept;

       this.duties = duties;

   }

   public String toString(){

       return name + ":" + dept + ":" + duties;

   }

}

 

임의의 파일에 객체 스트림을 연결하여 객체를 읽고 기록해 봅니다.

아래의 예는 객체를 기록한 후 다시 읽어내는 예제입니다.

 

----------------------------------------------------------------------

SerialObject.java

 

 

import java.io.*;

 

public class SerialObjectTest {

      

   public static void main(String[] args) throws Exception {

 

     FileOutputStream fileout = new FileOutputStream("test.txt");

      ObjectOutputStream out = new ObjectOutputStream(fileout);

       SerialObject se1 = new SerialObject("김언어", "개발부", "팀장");

       SerialObject se2 = new SerialObject("김서리", "자금부", "부장");

       SerialObject se3 = new SerialObject("이회계", "경리부", "차장");

       out.writeObject(se1);

       out.writeObject(se2);

       out.writeObject(se3);

       out.close();

 

       FileInputStream filein = new FileInputStream("test.txt");

       ObjectInputStream in = new ObjectInputStream(filein);

       SerialObject iso1 = (SerialObject)in.readObject();

       SerialObject iso2 = (SerialObject)in.readObject();

       SerialObject iso3 = (SerialObject)in.readObject();

       System.out.println(iso1.toString());

       System.out.println(iso2.toString());

       System.out.println(iso3.toString9));

       in.close();

   }

}

 

 

test.txt파일에 파일출력스트림을 생성합니다.

그리고 이 파일출력스트림을 Object출력스트림으로 변환합니다.

스트림을 열었다면 implements Serializable로 구현된 객체를 만들어야합니다.

위의 예제에서는 3개의 객체를 만들었습니다

그리고 이 객체를 test.txt파일에 객체가 3개이니 3번 기록 해야 합니다.

마지막으로 출력스트림을 닫습니다.

소스의 이 부분까지 수행되면 test.txt가 만들어지고 객체 3개가 순서대로 기록되게 됩니다.

 

입력된 객체를 읽어 내기 위해서 test.txt파일에 파일입력스트림을 생성합니다.

그리고 생성된 파일입력스트림을 Object입력스트림으로 변환합니다.

변환된 Object입력스트림으로 객체를 읽어냅니다.

앞에서 3개 입력했으니 3번만 읽어 내도록 합니다.

그리고 Object입력스트림으로 읽었을 때

반환형이 Object형이기 때문에 강제 Downcasting시켜야 합니다.

마지막으로 Object입력스트림을 닫으시면 모든 작업은 끝납니다.

 

 

Object스트림은 스트림의 한 종류입니다.

직렬화 된 객체를 보낼 수 있는 스트림이라고 말할 수 있습니다. 

객체를 객체출력스트림에 쓸 때는 ObjectOutputStream 클래스의 writeObject() 메서드를 사용합니다.

writeObject()의 원형의 다음과 같습니다.

 

public final void writeObject(Object obj) throws IOException

 

 writeObject()메서드는 인자로 넘어 온 객체가 Serializable인터페이스나 Externalizable 인터페이스를 구현했는지 검사합니다.

 

주어진 객체가 Serializable 인터페이스를 구현했다면,

writeObject()메서드는 자동으로 객체의 상태를 스트림에 기록해 줍니다.

만약 객체가 Serializable이나 Externalizable인터페이스 중 어느것도 구현하지 않았다면, NotSerializableException을 발생시킵니다.

 

스트림에 직렬화 되어있는 객체는 ObjectInputStream 클래스의 readObject() 메서드를 사용해서 복원 할 수 있습니다.

readObject()의 원형은 다음과 같습니다.

 

public final Object readObject() throws IOException, ClassNotFoundException

 

 readObject()메서드는 연결된 스트림으로부터 객체의 상태 정보를 읽어 내고,

writeObject()메소드와 마찬가지로 readObject()메서드 역시 객체가 Serializable 인터페이스를 구현했다면 스트림에 쓰여져 있던 객체의 상태 정보를 기반으로 자동으로 새로운 객체를 복원해 줍니다.

 

 

* transient 키워드

 

객체직렬화 전후에 보존하고 싶지 않은 멤버변수가 있을 경우에는, 해당 멤버변수에 transient 키워드를 사용함으로써 객체직렬화시 내용을 저장하지 않을 수 있다.

예를 들어 패스워드나 중요한 정보는 객체직렬화로 저장을 하게되면, 복원시에 누구라도 내용을 도로 알아낼 수가 있다.

따라서, 이러한 정보를 갖게되는 멤버변수는 transient 키워드를 사용해서 선언해주는 것이 좋다.

 

스트림을 이용해서 직렬화 할 때 객체의 모든 상태정보를 직렬화하게 됩니다.

하지만, 클래스를 디자인하다 보면 순간적으로 사용하고 버리는 필요없는 정보도 있습니다. 이러한 정보를 제외 시키기 위해서 transient키워드를 사용합니다.

 

클래스를 만들다 보면 중요하지는 않지만 전역 변수로 사용하기 위해서 어쩔 수 없이 멤버변수로 만드는 경우가 있습니다.

저장할 필요가 없다고 생각된다면 접근지정자 다음에 transient를 붙이면 직렬화에서

제외되어 버립니다.

 

객체를 직렬화 하는데 제외하겠다는 의미 이외에는 별다른 개념은 없습니다.

직렬화 될 필드가 static, transient로 선언되어 있으면 직렬화할 때 제외됩니다.

static변수는 공유메모리 개념을 가지고 있기 때문에 직렬화 할 때 제외 됩니다.

 

 

---------------------------------------------------------------------------------------------------------

TransientTest.java

 

 

import java.io.*;

 

public class TransientTest implements Serializable
{


     // 멤버변수
     private String name;
     transient String passwd;

 

 // 생성자
 public TransientTest(String s, String p)
 {
      name = s;
      passwd = p;
      System.out.println("생성자가 호출되었습니다: " + name);
 }

 

 // toString() 메서드를 오버라이드하여
 // println() 메서드에서 사용할때, 내용을 출력하도록 변경
 public String toString()
 {
      return "이름은 " + name+ " : 패스워드 : " + passwd;
 }

 

 public static void main(String args[])
 {
      TransientTest tt1, tt2;
      tt1 = new TransientTest("김가방","1234");
      tt2 = new TransientTest("이치민","0011");

  try
  {
       // 객체직렬화로 파일에 저장하기 위해
       // FileOutputStream에서 ObjectOutputStream 생성
       ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("TransientTest.ser"));

      

       // writeObject() 메서드를 사용하여 객체 저장
       out.writeObject(tt1);
       out.writeObject(tt2);
       out.close();

 

       // 객체직렬화로 파일에 저장된 객체를 복원하기 위해
       // FileInputStream에서 ObjectInputStream 생성
       ObjectInputStream in = new ObjectInputStream(new FileInputStream("TransientTest.ser"));

   

       TransientTest tt3, tt4;

 

       // 해당 스트림에서 readObject() 메서드를 호출
       tt3 = (TransientTest)in.readObject();
       tt4 = (TransientTest)in.readObject();

       System.out.println("다시 복원합니다");

 

        // 내용을 출력한다
       System.out.println(tt3);
       System.out.println(tt4);
  }catch(Exception e) {e.printStackTrace();}
 }
}

 

 

======================================================

출력내용

 

생성자가 호출되었습니다: 김가방
생성자가 호출되었습니다: 이치민
다시 복원합니다
이름은 김가방 : 패스워드 : null
이름은 이치민 : 패스워드 : null

-----------------------------

name 멤버변수는 그대로 복원이 되었으나,

transient로 되어있는 passwd 멤버변수는 null값이 들어있다.

 

 

 

* Externalizable

 

객체직렬화의 또 다른 방법으로는 Externalizable인터페이스를 사용하는 것입니다.

그 기본 개념은 Serializable과 같습니다.

Externalizable자체가 Serializable인터페이스를 상속한 인터페이스이기 때문입니다. 인터페이스는 인터페이스 끼리는 상속의 개념이 적용됩니다.

 

그래서 그 Externalizable의 원형은 다음과 같습니다.

 

public interface Externalizable extends Serializable {

   public void writeExternal(ObjectOutput out) throws IOException;

 

   public void readExternal(ObjectInput in) throws IOException,

                                                                             ClassNotFoundException;

}

 

Externalizable 인터페이스는 2개의 메서드를 구현해야만 사용 가능합니다.

그리고 Serializable보다 미세한 직렬화를 다루기 위해서 사용됩니다.

Serializable에서는 자동으로 데이터가 기록되지만

Externalizable에서는 기록하는 부분을 직접 제어합니다.

 

이 때 기록하는 부분은 writeExternal() 메서드에 구현을 하며

읽어내는 부분은 readExternal() 메서드에 만들어 줍니다.

writeExternal() 메서드에서 사용자가 임의로 기록하는 방법을 구현했다면

읽어내는 해답을 갖고 있는 것은 writeExternal()을 구현한 개발자 자신입니다.

거의 암호화의 개념에 가깝다.

 

 

-------------------------------------------------------------------------

ExternalObject.java

 

 

import java.io.*;

 

public class ExternalObject implements Externalizable {

 

   private int dept;               // 부서

   private String name;       // 이름

   private float duties;         // 직책

 

  public ExternalObject(){}

 

   public ExternalObject(int dept, String name, float duties) {

       this.dept = dept;

       this.name = name;

       this.duties = duties;

   }

   public void readExternal(ObjectInput in) throws IOException,

                                                                             ClassNotFoundException

{

       System.out.println("readExternal() 메서드입니다.");

       dept = in.readInt();

       name = (String)in.readObject();

       duties = in.readFloat();

   }

   public void writeExternal(ObjectOutput out) throws IOException {

       System.out.println("writeExternal() 메서드입니다.");

       out.writeInt(dept);

       out.writeObject(name);

       out.writeFloat(duties);

   }

   public String toString(){

       return dept + ":" + name + ":" + duties;

   }

}

 

 

매개변수로 넘어오는 ObjectOutput의 객체 out을 이용하여 기록하고 싶은 부분을 차례대로 write해주고 있습니다.

물론 필요로 하는 대부분의 write메서드는 이미 존재합니다.

여기서는 간단히 int, String, float만을 기록하였지만

ObjectOutput 인터페이스가 제공해주는 writeBoolean, writeByte, writeBytes, writeChar, writeChars, writeDouble, writeFloat, writeInt, writeLong, writeShort, writeUTF 메서드들을 전부 사용할 수 있습니다.

그리고, 다시 이것을 읽어 오는 부분은 기록한 차례대로 읽어오면 됩니다.

 

정확하게 순서를 맞추어 호출해 주어야 합니다.

만약 이것의 순서를 바꾼다면  에러를 만나게 될 것입니다.

그리고 마지막으로 직렬화된 데이터를 읽어들여서 객체를 만들기 위해 인자없는 생성자가 필요합니다.

이것을 만들어주지 않으면 에러메시지에서 인자없는 생성자를 요구할 것입니다

 

 

---------------------------------------------------------------------------

ExternalObjectTest.java

 

 

import java.io.*;

 

public class ExternalObjectTest {

      

   public static void main(String[] args) throws IOException, ClassNotFoundException{

 

       FileOutputStream fileout= new FileOutputStream("exTest.txt");

       ObjectOutputStream out= new ObjectOutputStream(fileout);

       ExternalObject eo1 = new ExternalObject(1, "김사양", 170.25f);

       ExternalObject eo2 = new ExternalObject(2, "이거지", 190.01f);

       ExternalObject eo3 = new ExternalObject(3, "삼다수", 180.34f);

       out.writeObject(eo1 );

       out.writeObject(eo2 );

       out.writeObject(eo3 );

       out.close();

 

       FileInputStream filein = new FileInputStream("exTest.txt");

       ObjectInputStream in = new ObjectInputStream(filein);

       ExternalObject eso1 = (ExternalObject)in.readObject();

       ExternalObject eso2 = (ExternalObject)in.readObject();

       ExternalObject eso3 = (ExternalObject)in.readObject();

       System.out.println(eso1.toString());

       System.out.println(eso2.toString());

       System.out.println(eso3.toString());

       ois.close();

   }

}

Adobe Flex 2 제품군에는 개발자가 서비스 지향 RIA(리치 인터넷 애플리케이션)를 제작 및 관리하는 방식에 대한 몇 가지 중요한 변경 사항이 적용되었습니다. Flex 패키징에 적용된 변경 사항으로 인해 서버측 구성 요소의 유무에 관계없이 Flex 애플리케이션을 제작하고 배포할 수 있지만 Flex Data Services의 여러 가지 새로운 기능을 이용하면 더욱 가치 있는 작업을 수행할 수 있습니다. 이 문서에서는 Flex Data Services 2의 새로운 기능에 대해 간단히 설명하고 특히 RPC(원격 프로시저 호출) 서비스가 Flex 1.5와 최신 버전의 Flex에서 어떻게 다른지에 대해 중점적으로 설명합니다.

Flex Data Services 2 메시징 프레임워크에는 RPC 서비스 요청을 강력하게 만드는데 도움이 되는 몇 가지 새로운 서비스 기능이 들어 있습니다. 첫째, 각 채널은 요청을 전송하기 전에 서버와 성공적으로 통신할 수 있는지를 확인합니다. 둘째, 개발자는 각 엔드포인트에 대해 여러 채널을 지정할 수 있기 때문에 보다 유연한 구성이 가능하며 첫 번째 채널을 사용할 수 없는 경우에 장애 복구가 가능합니다. 메시징 프레임워크는 클러스터된 환경도 지원하기 때문에 RPC 서비스에서 중복 환경을 활용하여 강력한 서비스를 제공할 수 있습니다.

또한 Flex Data Services 2는 애플리케이션의 관리 성능을 향상시켜 주는 다양한 기능을 제공합니다. 개발자는 서비스별 구성 파일을 통해 서비스에 대상을 추가하거나 제거할 수 있으며, 채널 엔드포인트를 구성하고, 보안 설정을 통해 서비스에 대한 액세스를 제어하고, 강력한 애플리케이션을 위한 클러스터를 정의하고, 디버깅 지원을 위한 로깅을 세밀하게 조정할 수 있습니다. 또한 개발자는 새로운 구성 형식을 통해 기술 요구 사항이 변경됨에 따라 새로운 채널, 서비스, 어댑터, 로그인 명령 및 로깅 대상을 추가할 수 있습니다.

Flex 1.5는 RemoteObject, HTTPServiceWebService MXML 태그를 통해 원격 프로시저에 비동기 호출을 보내는 세 가지 API를 제공합니다. 이러한 서비스를 RPC 서비스라고 하며 개발자는 이를 사용하여 SOA(서비스 지향 아키텍처) 애플리케이션을 구축할 수 있습니다. Flex Data Services 2는 이러한 RPC 서비스를 계속 지원하며 새로운 기능 및 새로운 프로그래밍 모델을 제공하는 두 가지 새로운 데이터 서비스를 제공합니다. 모든 서비스는 새로운 버전의 ActionScript(ActionScript 3.0)에서 제공하는 공통 메시징 아키텍처를 기반으로 합니다. 이 문서에서는 Flex 1.5와 Flex 2 간의 차이점에 대해 설명하고 Flex Data Services 2 RPC 서비스에서 이러한 향상된 기능이 어떻게 활용되는지에 대해 설명합니다.


RPC 서비스와 메시징 아키텍처

Flex 2의 새로운 메시징 아키텍처에는 RPC 서비스에 대한 몇 가지 새로운 개념이 도입되었습니다. 이 섹션에서는 Flex 1.5와의 중요한 차이점에 대해 설명하고 Flex 2에서 상응하는 기능에 대해 간단히 설명합니다.

Flex 1.5에서 개발자는 MXML 태그를 사용하여 RPC 서비스에 연결합니다. 개발자는 요청을 전송할 엔드포인트와 서비스 이름 등과 같은 몇 가지 속성을 지정합니다. Flex 1.5에서 각각의 RPC 서비스는 서로 다른 기술을 사용하기 때문에 각 서비스마다 구성 옵션과 용어가 다릅니다. 예를 들어 RemoteObject 태그는 명명된 또는 명명되지 않은 소스를 지정하고 AMF(Action Message Format) 게이트웨이에 연결하지만 HTTPServiceWebService 태그는 명명된 서비스 또는 원시 URL을 사용하고 텍스트 기반 쿼리 매개 변수 또는 XML을 사용하여 HTTP 프록시에 연결합니다.

Flex 2는 이런 모델에 몇 가지 향상된 기능을 새로 추가하여 여러 채널 간에 일관성 유지를 강화하고 개발자에게는 보다 큰 유연성을 제공합니다. 클라이언트측에서 개발자는 MXML 태그나 순수한 ActionScript API를 사용하여 서비스 연결을 선언할 수 있습니다. 연결이 클라이언트측 전용인 경우 서비스의 URI는 매개 변수로 지정됩니다. Flex Data Services를 사용하여 연결을 구성하는 경우 개발자는 원하는 서비스와 연결된 논리적 대상을 지정할 수 있습니다. 서비스란 매체를 통한 웹 서비스 호출이나 메시징 통합 등과 같은 다양한 고유 기능을 나타냅니다. 서버측에서 모든 대상은 구성 파일을 통해 구성됩니다. 대상에는 대상이 연결되는 엔드포인트에 대한 정보, 통신에 사용되는 채널, 모든 연결에 사용되는 다양한 메시징 옵션이 포함됩니다. 채널이란 일반적으로 채널에 폴링 사용 여부 및 원하는 폴링 간격 등과 같은 몇 가지 옵션과 프로토콜을 지정하는 통신 메커니즘을 나타냅니다.

제공된 다양한 서비스와의 모든 통신은 메시지를 적절한 서비스로 연결하는 공통 메시지 브로커에 의해 중재됩니다. 메시지 브로커는 메시지 유형과 선택한 대상을 사용하여 적합한 서비스를 선택하고 서비스에서 메시지를 처리할 수 있도록 합니다. RemoteObject 메시지는 Remoting Service로 라우팅되고 HTTPServiceWebService 메시지는 Proxy Service로 라우팅됩니다. 표 1에서는 Flex 1.5의 서비스 개념과 Flex Data Services 2의 서비스 개념을 비교해서 보여줍니다.

표 1. Flex 1.5와 Flex Data Services 2의 개념 비교
Flex 1.5 서비스 개념 Flex Data Services 2 서비스 개념

명명된 서비스 및 명명되지 않은 서비스

Flex 1.5에서 개발자는 주로 서비스에 이름을 제공하여 앨리어스 역할을 수행하도록 하지만 클라이언트에서도 명명되지 않은 방식으로 서비스의 세부 정보(예: 타사 URL이나 객체의 소스)를 제공할 수 있습니다.

대상

대 상은 Flex 1.5의 명명된 서비스에 해당합니다. 이제 서비스란 용어는 포괄적인 기능을 나타내며, 대상은 궁극적인 엔드포인트를 나타냅니다. 모든 요청은 관련 서비스(Remoting Service, Proxy Service, Message Service 및 Data Service)의 대상으로 전송되어야 합니다.

프록시

HTTPServiceWebService 요청에 대한 목표 엔드포인트는 일반적으로 원격 타사 서비스입니다. 하지만 Flash® Player의 보안 샌드박스는 애플리케이션이 로드된 도메인으로 요청을 제한합니다. Flex 1.5에서는 각각의 원격 사이트에서 crossdomain.xml 정책 파일을 요청하지 않도록 하기 위해서 요청을 적합한 엔드포인트로 중계할 수 있는 내장 Proxy Servlet이 제공되었습니다.

Proxy Service

HTTPServiceWebService에서는 메시지 브로커를 사용하여 Proxy Service를 통해 요청을 중계하지만 보안 샌드박스 제한은 여전히 모든 서비스에 적용됩니다.

Proxy Service는 더 이상 기본값으로 사용되지 않으며, useProxy 속성의 기본값은 false입니다. useProxyfalse로 설정된 경우 메시지 브로커는 연결되지 않고 destination 속성이 사용되지 않습니다. 대신 개발자는 HTTPService 태그에 대한 url 속성을 설정하거나 WebService 태그에 대한 wsdl 속성을 지정해야 합니다. 개발자는 특별한 직접 채널을 선택하여 타사 엔드포인트에 대한 비메시징 기반 요청을 만듭니다.

프록시를 사용하려면 개발자는 HTTPService 또는 WebService에서 useProxytrue로 설정해야 합니다. HTTPService 또는 WebService에서 대상을 설정하면 useProxy가 자동으로 true로 설정됩니다.

Proxy Service를 사용할 때는 모든 채널에서 요청을 만들 수 있습니다. 채널은 메시지 브로커에 엔드포인트로 등록됩니다. HTTP 기반 채널은 메시지를 메시지 브로커 서블릿으로 전송합니다. RTMP(Real Time Message Protocol) 기반의 채널은 메시지를 전용 TCP/IP 포트로 전송합니다. 메시지 브로커는 이러한 포트에서 수신 대기 중인 소켓을 관리합니다.

AMF 게이트웨이

RemoteObject 요청에 대한 엔드포인트는 AMF 게이트웨이 서블릿입니다.

Remoting Service

RemoteObject에 대한 전용 채널은 없습니다. Remoting Service에 대한 요청은 모든 채널에서 만들 수 있습니다.


RPC 서비스 구성

Flex 1.5에서 컴파일러 및 런타임 서비스에 대한 구성 설정은 단일 구성 파일인 flex-config.xml에 포함되어 있습니다. 이 구성 파일에서는 RemoteObject 태그에 대한 <remote-objects>, WebService 태그에 대한 <web-service-proxy>HTTPService 태그에 대한 <http-service-proxy> 등의 세 가지 섹션이 RPC 서비스에 사용됩니다. Flex는 컴파일 시에 서비스 구성 정보를 사용하여 MXML 태그에 대한 코드 및 태그 속성의 유효성 검사를 위한 코드를 생성합니다. 다음 샘플에서는 Flex 1.5 구성 파일인 flex-config.xml의 구조를 보여줍니다.

 <flex-config>
<debugging />
<compiler />
<cache />
<flash-player />
<web-service-proxy />
<http-service-proxy />
<remote-objects />
<logging />
<fonts />
</flex-config>

Flex 1.5 AMF 게이트웨이에는 자체 구성 파일인 gateway-config.xml도 있습니다. 개발자는 이 파일을 사용하여 RemoteObject 요청을 처리하는 서비스 어댑터, 사용자 정의 서비스 보안을 위한 로그인 명령 등을 포함하여 몇 가지 기능을 사용자 정의할 수 있습니다. 다음의 코드에서는 Flex 1.5 RemoteObject 구성 파일인 gateway-config.xml의 구조를 보여줍니다.

 <gateway-config>
<service-adapters />
<logger />
<security />
<serialization />
<translator />
<redirect-url />
<lowercase-keys />
</gateway-config>

Flex Data Services 2에서는 별개의 구성 파일인 services-config.xml에 서비스 관련 구성 정보가 저장됩니다. Flex 1.5에서 서비스와 관련된 세 가지 섹션이 flex-config.xml에서 제거되었습니다. 그러나 컴파일러 특정 구성 설정은 이 파일에 그대로 유지됩니다. 캐싱 및 Flash Player 감지 등과 같은 기타 웹 계층 구성 설정은 flex-server-config.xml 파일에 저장됩니다. Flex 1.5 gateway-config.xml 파일은 더 이상 사용되지 않습니다. 새로운 공통 services-config.xml 파일은 모든 관련 게이트웨이 기능을 포함하며 설정을 모든 서비스에 적용합니다.

개발자는 services-config.xml 구성 파일을 사용하여 외부 파일로부터 개별적인 서비스 정의를 포함시킬 수 있습니다. 따라서 서비스 특정 대상을 격리시키고 기본 서비스 구성 파일의 크기를 줄일 수 있게 되며 결과적으로 보안, 채널 및 로깅 설정 등과 같은 공통 섹션에 쉽게 액세스할 수 있습니다. 다음 샘플에서는 각 서비스가 개별적인 include 파일에 포함되어 있는 Flex Data Services 2 구성 파일인 services-config.xml의 일반적인 구조를 보여줍니다.

 <services-config>
<services>
<service-include file-path="remoting-service.xml" />
<service-include file-path="proxy-service.xml" />
</services>
<security />
<channels />
<clusters />
<logging />
<system />
</services-config>

Flex 애플리케이션에서 Flex Data Services가 사용되는 경우 컴파일 시에 services-config.xml 구성 파일이 필요합니다. RPC 서비스의 경우에 이것은 RemoteObject나 프록시 기반 WebService 또는 HTTPService를 사용하는 모든 애플리케이션에 적용되는 사항입니다.

mxmlc 컴파일러는 flex-config.xml 파일 또는 명령줄 스위치를 사용하여 서비스 구성 파일을 지정할 수 있습니다. 각 서비스 대상에 대해 사용할 채널을 클라이언트에 알려주는 코드를 생성해야 하기 때문에 컴파일 시에 서비스 구성 정보가 반드시 필요합니다. 다음 구성 코드는 flex-config.xml을 사용하여 mxmlc 컴파일러를 Flex Data Services 2 구성 파일에 지정합니다.

 <flex-config>
<compiler>
<services>
services-config.xml
</services>
...
</compiler>
</services-config>

다음 구성 코드는 명령줄을 사용하여 mxmlc 컴파일러를 Flex Data Services 2 구성 파일에 지정합니다.

   mxmlc --load-config=flex-config.xml --services=services-config.xml MyApp.mxml

표 2에서는 Flex 1.5와 Flex Data Services 2의 구성상 차이점을 보여줍니다.

Flex 1.5와 Flex Data Services 2의에서 구성상의 차이점

Flex 1.5 서비스 구성

게이트웨이 URL 및 프록시 URL

다음에 대한 flex-config.xml의 사용:

  • HTTPService, WebService

{context.root}/flashproxy

  • RemoteObject

mxmlc 명령줄 스위치

  • HTTPService, WebService

-proxyurl=/myapp/flashproxy

  • RemoteObject

-gatewayurl=/myapp/amfgateway

Flex Data Services 2 서비스 구성

채널

서 비스는 모든 채널 엔드포인트를 사용하여 메시지를 처리합니다. 서비스 특정 엔드포인트 설정은 필수가 아니며 대신 개발자가 새 채널을 만들 수 있습니다. 채널은 서비스 구성 파일인 services-config.xml에 정의되어야 하며, 채널은 서비스와 독립적으로 정의됩니다.

<channel-definition id="my-amf" ...>
<endpoint uri="http://www.abc.com/messagebroker/amf" ... />
</channel-definition>

한 서비스에 대해 다음과 같은 기본 채널 집합을 설정할 수 있습니다.

 <service id="remoting-service" ...>
...
<default-channels>
<channel ref="my-amf"/>
</default-channels>
...
</service>

또한 대상은 고유 채널 목록을 지정하여 해당 서비스의 기본값을 무시할 수 있습니다.

 <destination id="MyManager">
<channels>
<channel ref="my-amf"/>
</channels>
...
</destination>

개별 채널은 명령줄에 지정할 수 없습니다. 그러나 mxmlc 명령줄 스위치를 통해 컴파일러를 완전한 서비스 구성 파일에 지정할 수 있습니다.

   --services=/flex/services-config.xml

Flex 1.5 서비스 구성

서비스 화이트 리스트

화이트 리스트는 클라이언트에 사용할 수 있는 서비스를 제한하는 데 사용됩니다.

HTTPServiceWebService 서비스는 궁극적인 타사 엔드포인트의 URL을 기반으로 화이트 리스트에 지정할 수 있습니다.

 <whitelist>
<unnamed>
<url>
http://www1.abc.com/*
</url>
<url>
https://www1.abc.com/*
</url>
</unnamed>
<named>
<service name="MyManager">
<url>
http://www.abc.com/mysvc/10/
</url>
</service>
</named>
</whitelist>

RemoteObject 객체는 원격 객체의 기본 소스를 기반으로 화이트 리스트에 지정할 수 있습니다.

 <whitelist>
<unnamed>
<source>*</source>
</unnamed>
<named>
<object name="WeatherService">
<source>
samples.WeatherService
</source>
<type>stateless-class</type>
</object>
</named>
</whitelist>

Flex Data Services 2 서비스 구성

서비스 대상

서비스에 대상을 추가하는 작업은 화이트 리스트에 어떤 항목을 추가하는 것과 같습니다.

Proxy Service 대상은 URL 패턴과 SOAP 엔드포인트를 지정하여 Flex 1.5의 명명되지 않은 서비스에 동일한 기능을 제공할 수 있습니다. 서비스의 기본 URL은 url 속성에 정의됩니다. 동적 또는 클라이언트 제공 URL은 dynamic-url 속성에 정의됩니다.

 <destination id="flickr">
<properties>
<url>
http://www.abc.com/mysvc/10/
</url>
<dynamic-url>
http://www1.abc.com/*
</dynamic-url>
<dynamic-url>
https://www1.abc.com/*
</dynamic-url>
</properties>
</destination>

defaultHttpdefaultHttps 대상이 Proxy Service에 대해 구성되어 있으므로 개발자는 대상을 지정하지 않고도 HTTPServiceWebService 태그를 사용할 수 있습니다. 하지만 defaultHttp 대상을 사용하는 HTTP 기반 URL과 defaultHttps를 사용하는 HTTPS 기반 URL을 사용하는 기본 대상에 대해서는 dynamic-url을 구성해야 합니다.

Java 어댑터를 사용하는 Remoting Service 대상은 더 이상 클라이언트에 소스를 지정할 수 없습니다. 개발자는 서버에서 각 Java 소스에 대해 고유한 대상을 등록해야 합니다.

 <destination id="WeatherService">
<properties>
<source>
samples.WeatherService
</source>
<stateful>false</stateful>
</properties>
</destination>

Flex 1.5 서비스 구성

프록시 및 AMF 게이트웨이 로깅

Flex 1.5에서 개발자는 서버의 gateway-config.xml 파일에서 로깅 설정을 수정하여 AMF 요청을 디버깅할 수 있습니다. 프록시 요청은 웹 계층 컴파일러에서 사용되는 기본 flex-config.xml 파일에서 로깅 설정을 수정하여 디버깅할 수 있습니다.

Flex Data Services 2 서비스 구성

로깅

메시지 브로커는 모든 서비스에 대한 통합 로깅 시스템을 갖고 있습니다. 개발자는 services-config.xml의 로깅 섹션을 이용하여 로깅 수준을 사용자 정의하고 정보를 특정 범주로 제한할 수 있습니다.

Flex 1.5 서비스 구성

클라이언트 로깅

Flex 1.5에서 개발자는 flex-config.xml의 디버깅 섹션에서 http-service-proxy-debug, web-service-proxy-debug 또는 remote-object-debug 설정을 true로 설정하여 클라이언트에서 서비스 요청을 디버깅할 수 있습니다. 개발자는 Flex Builder™ Network Monitor, Flash Remoting NetConnection Debugger 및 사용자 디렉토리에 있는 flashlog.txt 파일에 저장된 추적 정보의 디버깅 Flash Player 출력을 사용하여 클라이언트 로깅 정보를 확인할 수 있습니다.

Flex Data Services 2 서비스 구성

클라이언트 로깅 API 및 로그 대상

Flex 2의 mx.logging 패키지에는 새로운 클라이언트 로깅 라이브러리가 포함되어 있습니다. 모든 서비스는 이 클라이언트 로깅 API를 사용하여 정보를 보고합니다. 개발자는 로그 이벤트의 수준을 지정하고 이벤트의 범주를 필터링할 수 있습니다. 개발자는 로깅 대상을 활성화하여 특정 위치에 있는 정보를 볼 수 있습니다. 가장 일반적으로 사용되는 대상은 TraceTarget(mx.logging.targets 패키지)이며, 이 대상은 trace() 함수를 사용하여 정보를 보고합니다. Flash Player의 디버깅 버전은 추적 정보를 사용자 디렉토리에 있는 flashlog.txt 파일로 출력합니다.


Flex 1.5 서비스 구성

프록시 및 AMF 게이트웨이 로깅

Flex 1.5에서 개발자는 서버의 gateway-config.xml 파일에서 로깅 설정을 수정하여 AMF 요청을 디버깅할 수 있습니다. 프록시 요청은 웹 계층 컴파일러에서 사용되는 기본 flex-config.xml 파일에서 로깅 설정을 수정하여 디버깅할 수 있습니다.

Flex Data Services 2 서비스 구성

로깅

메시지 브로커는 모든 서비스에 대한 통합 로깅 시스템을 갖고 있습니다. 개발자는 services-config.xml의 로깅 섹션을 이용하여 로깅 수준을 사용자 정의하고 정보를 특정 범주로 제한할 수 있습니다.

Flex 1.5 서비스 구성

클라이언트 로깅

Flex 1.5에서 개발자는 flex-config.xml의 디버깅 섹션에서 http-service-proxy-debug, web-service-proxy-debug 또는 remote-object-debug 설정을 true로 설정하여 클라이언트에서 서비스 요청을 디버깅할 수 있습니다. 개발자는 Flex Builder™ Network Monitor, Flash Remoting NetConnection Debugger 및 사용자 디렉토리에 있는 flashlog.txt 파일에 저장된 추적 정보의 디버깅 Flash Player 출력을 사용하여 클라이언트 로깅 정보를 확인할 수 있습니다.

Flex Data Services 2 서비스 구성

클라이언트 로깅 API 및 로그 대상

Flex 2의 mx.logging 패키지에는 새로운 클라이언트 로깅 라이브러리가 포함되어 있습니다. 모든 서비스는 이 클라이언트 로깅 API를 사용하여 정보를 보고합니다. 개발자는 로그 이벤트의 수준을 지정하고 이벤트의 범주를 필터링할 수 있습니다. 개발자는 로깅 대상을 활성화하여 특정 위치에 있는 정보를 볼 수 있습니다. 가장 일반적으로 사용되는 대상은 TraceTarget(mx.logging.targets 패키지)이며, 이 대상은 trace() 함수를 사용하여 정보를 보고합니다. Flash Player의 디버깅 버전은 추적 정보를 사용자 디렉토리에 있는 flashlog.txt 파일로 출력합니다.


RPC 서비스 MXML API에 대한 변경 사항

Flex 2의 RPC 서비스 API는 기본적으로 Flex 1.5와 같습니다. 그러나 공통 메시징 아키텍처를 이용하게 됨으로써 서비스 통신 및 명명 규칙과 관련된 몇 가지 속성에 변경 사항이 있습니다. 표 3에서는 Flex 1.5에서 Flex Data Services 2로 버전업되면서 MXML API에 어떤 변경 사항이 있는지를 간단히 보여줍니다.

표 3. Flex 1.5에서 Flex Data Services 2로 버전업되면서 MXML API에 적용된 변경 사항 개요
Flex 1.5 MXML API Flex Data Services 2 MXML API

mx:RemoteObject

  • source 속성은 명명되지 않은 원격 객체에서 사용됩니다. 이 속성은 화이트 리스트 구성에서 명명된 객체를 등록하지 않고 RemoteObject에 대한 동적 소스 이름을 정의합니다.
  • named 속성은 명명된 원격 객체에서 사용됩니다. 이 속성은 등록된 객체 소스에 앨리어스를 제공합니다.
  • protocol 속성은 AMF 게이트웨이에 연결할 때 HTTP와 HTTPS 중 어떤 프로토콜을 사용할지를 결정합니다.
  • 개발자는 endpoint 속성을 사용하여 AMF 게이트웨이의 사용자 정의 위치를 지정할 수 있습니다.

mx:RemoteObject

  • source 속성은 Remoting Service Java Adapter에 의해 무시됩니다. ColdFusion은 CFC 어댑터에서 클라이언트 지정 소스를 지원합니다.
  • named 속성은 더 이상 사용되지 않으며 대신 destination 속성을 사용해야 합니다.
  • protocol 속성은 지원되지 않습니다. 메시지 브로커에 연결하는 데 사용되는 프로토콜은 대상에 대해 선택된 채널에 따라 달라집니다.
  • endpoint 속성은 RemoteObject의 MXML API에 대해서만 지원됩니다. Flex 2에서는 개발자가 이 속성을 사용하여 컴파일 시에 서비스 구성 파일을 참조하거나 ChannelSet을 프로그래밍 방식으로 만들지 않고도 RemoteObject 대상의 엔드포인트를 신속하게 지정할 수 있습니다. 또한 RemoteObject 서비스에 이미 ChannelSet이 설정되어 있는 경우 기존 ChannelSet을 무시합니다. endpoint 속성은 Flex Data Services 2 또는 ColdFusion 7.0.2와 같이 Flex 2와 호환되는 엔드포인트여야 합니다. 엔드포인트 URL이 https로 시작하는 경우 SecureAMFChannel이 사용되고, 그렇지 않으면 AMFChannel이 사용됩니다.

mx:WebService

mx:HTTPService

  • protocol 속성은 프록시 또는 타사 엔드포인트에 연결할 때 HTTP와 HTTPS 중 어떤 프로토콜을 사용할지를 결정합니다.
  • serviceName 속성은 명명된 프록시 웹 서비스에서 사용됩니다.

모든 서비스

개발자는 WebService 또는 RemoteObject 작업이나 HTTPService 호출에 대한 result 속성을 사용하여 서비스에서 반환된 마지막 결과에 바인딩할 수 있습니다.

mx:WebService

mx:HTTPService

  • protocol 속성은 지원되지 않습니다. 메시지 브로커에 연결하는 데 사용되는 프로토콜은 대상에 대해 선택된 채널에 따라 달라집니다.
  • serviceName 속성은 더 이상 사용되지 않으며 대신 destination 속성을 사용해야 합니다.

모든 RPC 서비스

개발자가 결과 이벤트 핸들러를 지정할 수 있게 해주는 MXML result 이벤트 속성과 이름 충돌이 발생하지 않도록 WebService 또는 RemoteObject 작업이나 HTTPService 호출에 대한 result 속성 이름이 lastResult로 변경되었습니다.


RPC 서비스에서 채널 사용

채널은 클라이언트의 메시지를 메시지 브로커의 엔드포인트로 전달하는 역할을 수행합니다. 메시지를 전달하는 데 사용되는 프로토콜은 채널에 따라 다릅니다. 또한 채널은 ActionScript 3.0을 사용하는 Flex 애플리케이션에서 사용되는 유형인 클라이언트의 데이터 유형을 지원할 수 있어야 합니다. 채널은 요청을 직렬화(serialize)하고 응답을 역직렬화(deserialize)하는 클라이언트 구현과 요청을 역직렬화하고 응답을 직렬화하는 엔드포인트에서의 서버 구현으로 구성됩니다. Flex Data Services 2에는 서비스 기반 애플리케이션의 다양한 요구 사항을 지원하기 위한 몇 가지 채널이 제공됩니다.

  • AMF 채널: AMF는 ActionScript 객체를 효율적으로 직렬화하기 위해 사용되는 바이너리 메시지 형식입니다. AMFChannel은 Flash Player용 flash.net.NetConnection 클래스를 사용하여 HTTP를 통해 AMF 형식의 메시지를 AMFEndpoint로 비동기적으로 전송합니다. Flash Player 8.5에서는 AMF 3으로 알려진 새로운 버전의 AMF를 도입하여 새로운 ActionScript 3.0 언어를 제공하지만 이전 버전인 AMF 0도 계속 지원됩니다. 새로운 ActionScript 3 데이터 유형에 대한 지원 외에도 AMF 3에서는 공통적으로 발생하는 클래스 설명과 참조 문자열을 직렬화하여 보다 간결한 인코딩 형식을 사용하고 중복된 정보를 줄입니다. NetConnection(및 AMFChannel)은 기본적으로 AMF 3을 사용합니다. AMFChannel은 HTTP를 통한 바이너리 정보 사용이 가능하고 보안을 위해 암호화 기능을 사용할 필요가 없는 환경에서 모든 RPC 서비스에 대해 권장되는 채널입니다.
  • 보안 AMF 채널: 이 채널은 AMFChannel와 동일하지만 HTTPS를 사용하여 AMF 형식의 메시지를 AMFEndpoint로 전송하는 점이 다릅니다.
  • RTMP 채널: RTMP 도 AMF를 사용하여 ActionScript 객체를 직렬화하지만 RTMPEndpoint와의 영구적인 연결을 유지 관리하고 실시간 통신이 가능합니다. RPC 서비스는 단일 클라이언트/서버 요청/응답 모델을 사용하여 비동기적으로 만들어지기 때문에 실시간 통신은 필요하지 않습니다.
  • HTTP 채널: 이 채널은 flash.net.URLLoader를 사용하여 XML 형식의 메시지를 HTTP를 통해 HTTPEndpoint로 비동기적으로 전송하는 텍스트 기반 채널입니다. XML 형식은 강력한 형식의 ActionScript 3.0 데이터를 지원하며 공통적으로 발생하는 객체, 클래스 정의 및 문자열에 대한 참조별 직렬화와 같은 AMF 3의 다양한 최적화 기술을 포함합니다. 이 채널은 텍스트 전용 통신 요구 사항이 있는 환경에서 유용합니다.

    참고: flash.utils.IExternalizable 인터페이스는 아직 HTTPChannel에서 완전히 지원되지 않습니다. 따라서 객체 참조를 복구할 필요가 있는 mx.utils.ObjectProxymx.collections.ArrayCollection 인스턴스는 이 채널을 사용하여 직렬화하면 안 됩니다.

  • Secure HTTP 채널: 이 채널은 HTTPChannel 클래스와 동일하지만 HTTPS를 사용하여 메시지를 HTTPEndpoint에 전송한다는 점이 다릅니다.

ActionScript 3.0 데이터 유형 및 직렬화(serialization)

Flex 2 및 ActionScript 3.0에는 RemoteObject 및 Remoting Service를 사용하는 개발자에게 유용한 몇 가지 새로운 데이터 유형(Flex 애플리케이션에서 사용할 수 있는 데이터 유형)이 포함되어 있습니다.

XML

ActionScript 3.0에는 E4X 구문을 지원하는 새로운 내장 XML 데이터 유형이 포함되어 있습니다. 이러한 API는 매우 강력하고 유용한 기능을 제공합니다. flash.xml 패키지에서는 ActionScript 2에서 제공되는 기존의 XMLDocumentXMLNode 클래스도 사용할 수 있습니다. 클라이언트 XMLflash.xml.XMLDocument 인스턴스는 모두 Java org.w3c.dom.Document 인스턴스와 같은 메시지 엔드포인트에 의해 역직렬화됩니다. org.w3c.dom.Document의 서버 인스턴스는 새로운 XML 데이터 유형의 인스턴스로 클라이언트에서 역직렬화됩니다. 그러므로 개발자는 서버를 통해 왕복하는 객체의 경우 모든 flash.xml.XMLDocument 데이터가 XML로 반환된다는 점에 주의해야 합니다.

강력한 형식의 객체

Flex 1.5에서 사용자는 Object.registerClass를 사용하여 사용자 정의 클라이언트 및 서버 데이터 유형을 매핑했습니다. ActionScript 3.0에서 이 기능은 flash.net.registerClassAlias로 변경되었습니다. 이 API는 클라이언트 클래스에 대해 앨리어스를 등록합니다. 그러면 클래스의 인스턴스가 직렬화될 때 앨리어스가 포함됩니다. 등록된 앨리어스가 없는 경우 인스턴스는 익명 객체로 전송됩니다. 또한 앨리어스를 사용하면 클라이언트에서 클라이언트 클래스를 다른 이름의 서버 클래스로 매핑할 수도 있습니다. 하지만 개발자는 혼란을 피하기 위해 동일한 이름을 사용하는 것이 가장 좋습니다. 인스턴스가 올바르게 역직렬화되도록 하려면 인스턴스가 서버에 전송되거나 클라이언트에서 수신되기 전에 클라이언트 클래스 앨리어스를 등록해야 합니다.

Flex 2 MXML 컴파일러에는 클래스의 느슨한 초기화(lazy-initialization) 및 종속성 기반 링커(linker)와 같은 몇 가지 최적화 기술이 포함되어 있습니다. 여기서 registerClassAlias에 대한 호출은 데이터 요청이 발생하기 전에 수행되어야 하며, 클라이언트 클래스에 대한 종속성은 컴파일된 애플리케이션에 연결되도록 코드에 작성되어야 합니다. 이러한 프로세스를 보다 쉽게 수행할 수 있도록 mxmlc 컴파일러에는 RPC 서비스에서 클래스를 즉시 사용할 수 있도록 보장하는 특별한 [RemoteClass] 메타데이터 태그에 대한 지원이 포함되어 있습니다. ActionScript 3.0 구문의 다음 코드에서는 Flex 2 애플리케이션에서 원격 클래스를 매핑합니다.

   package com.mycompany
{

[RemoteClass(alias="com.mycompany.Employee")]
public class Employee
{

}

}

Number, int 및 uint

ActionScript 3.0에서는 배정밀도 부동 소수점 Number 유형(int(부호 있는 정수) 및 uint(부 호 없는 정수)) 외에도 두 가지 새로운 정수 유형이 제공됩니다. 새로운 AVM+(ActionScript Virtual Machine)는 정수를 가변 길이로 인코딩된 29비트 정수로 보관하며, 더 큰 정수 값은 Number를 사용하여 보관합니다. intuint 유형은 서버에서 java.lang.Integer를 사용하여 역직렬화됩니다. Number는 서버에서 계속해서 java.lang.Double로 역직렬화됩니다. Remoting Service의 Java 어댑터는 메서드를 호출하고 강력한 형식의 인스턴스를 호출할 때 느슨한 유형의 마샬링(marshalling) 규칙을 사용하기 때문에 모든 숫자는 사용자의 API에 필요한 형식으로 올바르게 캐스팅됩니다.

사용자 정의 직렬화 및 flash.utils.IExternalizable

ActionScript 3.0에는 개발자가 자신만의 강력한 형식의 클래스에 대한 직렬화를 제어할 수 있게 해주는 새로운 인터페이스가 포함되어 있습니다. flash.utils.IExternalizable을 구현하는 인스턴스는 readExternalwriteExternal 메서드의 구현에 따라 직렬화됩니다. 클라이언트 클래스는 등록된 앨리어스가 있어야 하며, 해당 서버 클래스는 인스턴스를 역직렬화하는 데 사용할 수 있어야 합니다. 이 유형에 대한 직렬화 형식은 완전히 사용자 정의가 가능하기 때문에 직렬화를 올바르게 수행하기 위해 서버 클래스를 사용할 수 있어야 합니다.

데이터 바인딩 및 MXML 구성 요소

서 비스가 Flex 2 구성 요소에 데이터 공급자로 참여하기 위해서는 데이터 유형이 올바른 이벤트로 전달되어 데이터 바인딩과 변경 사항의 전달을 지원해야 합니다. 따라서 데이터 바인딩을 쉽게 수행하도록 데이터 유형 직렬화 규칙이 일부 변경되었습니다.

<mx:Model/>

MXML Model 태그를 사용하면 일반 객체 속성을 사용하여 데이터 바인딩 작업을 수행할 수 있습니다. Model 태그는 서버에서 java.util.HashMap으로 역직렬화되는 ActionScript 유형인 mx.utils.ObjectProxy로 지원됩니다.

참고: mx.utils.ObjectProxy 클래스는 flash.utils.IExternalizable을 구현하므로 이 유형은 아직 HTTPChannel 클래스에 의해 완전히 지원되지 않습니다. 다른 유형과 IExternalizable 유형의 컨텐츠 사이에서 객체 참조는 유지되지 않습니다.

mx.collections.ArrayCollection

컬 렉션은 Flex 2에서 포괄적으로 사용됩니다. 컬렉션은 컬렉션 구조의 변경 사항이 Flex 구성 요소(특히 UI 구성 요소)에 반영되고 구성 요소의 변경 사항이 컬렉션 구조에 반영될 수 있도록 배열이 올바른 이벤트로 전달될 수 있는 이벤트 프레임워크를 제공합니다. 이제 Flex Data Services는 클라이언트 mx.collections.ArrayCollection 인스턴스를 서버의 java.util.ArrayList로 매핑하고 서버측 java.util.Collection 구현은 클라이언트의 mx.collections.ArrayCollection으로 매핑합니다. 서버로부터의 응답에 원시 클라이언트 Array가 필요한 경우 원시 Java 배열(예: java.lang.Object[])을 반환할 수 있습니다. 또한 클라이언트 ActionScript Arrays는 서버에서 원시 java.lang.Object[] 배열로 역직렬화됩니다.

참고: mx.collections.ArrayCollectionflash.utils.IExternalizable을 구현합니다. 여기서 이 유형은 아직 HTTPChannel 클래스에 의해 완전히 지원되지 않습니다. 다른 유형과 IExternalizable 유형의 컨텐츠 사이에 객체 참조는 유지되지 않습니다.


Number, int 및 uint

ActionScript 3.0에서는 배정밀도 부동 소수점 Number 유형(int(부호 있는 정수) 및 uint(부 호 없는 정수)) 외에도 두 가지 새로운 정수 유형이 제공됩니다. 새로운 AVM+(ActionScript Virtual Machine)는 정수를 가변 길이로 인코딩된 29비트 정수로 보관하며, 더 큰 정수 값은 Number를 사용하여 보관합니다. intuint 유형은 서버에서 java.lang.Integer를 사용하여 역직렬화됩니다. Number는 서버에서 계속해서 java.lang.Double로 역직렬화됩니다. Remoting Service의 Java 어댑터는 메서드를 호출하고 강력한 형식의 인스턴스를 호출할 때 느슨한 유형의 마샬링(marshalling) 규칙을 사용하기 때문에 모든 숫자는 사용자의 API에 필요한 형식으로 올바르게 캐스팅됩니다.

사용자 정의 직렬화 및 flash.utils.IExternalizable

ActionScript 3.0에는 개발자가 자신만의 강력한 형식의 클래스에 대한 직렬화를 제어할 수 있게 해주는 새로운 인터페이스가 포함되어 있습니다. flash.utils.IExternalizable을 구현하는 인스턴스는 readExternalwriteExternal 메서드의 구현에 따라 직렬화됩니다. 클라이언트 클래스는 등록된 앨리어스가 있어야 하며, 해당 서버 클래스는 인스턴스를 역직렬화하는 데 사용할 수 있어야 합니다. 이 유형에 대한 직렬화 형식은 완전히 사용자 정의가 가능하기 때문에 직렬화를 올바르게 수행하기 위해 서버 클래스를 사용할 수 있어야 합니다.

데이터 바인딩 및 MXML 구성 요소

서 비스가 Flex 2 구성 요소에 데이터 공급자로 참여하기 위해서는 데이터 유형이 올바른 이벤트로 전달되어 데이터 바인딩과 변경 사항의 전달을 지원해야 합니다. 따라서 데이터 바인딩을 쉽게 수행하도록 데이터 유형 직렬화 규칙이 일부 변경되었습니다.

<mx:Model/>

MXML Model 태그를 사용하면 일반 객체 속성을 사용하여 데이터 바인딩 작업을 수행할 수 있습니다. Model 태그는 서버에서 java.util.HashMap으로 역직렬화되는 ActionScript 유형인 mx.utils.ObjectProxy로 지원됩니다.

참고: mx.utils.ObjectProxy 클래스는 flash.utils.IExternalizable을 구현하므로 이 유형은 아직 HTTPChannel 클래스에 의해 완전히 지원되지 않습니다. 다른 유형과 IExternalizable 유형의 컨텐츠 사이에서 객체 참조는 유지되지 않습니다.

mx.collections.ArrayCollection

컬 렉션은 Flex 2에서 포괄적으로 사용됩니다. 컬렉션은 컬렉션 구조의 변경 사항이 Flex 구성 요소(특히 UI 구성 요소)에 반영되고 구성 요소의 변경 사항이 컬렉션 구조에 반영될 수 있도록 배열이 올바른 이벤트로 전달될 수 있는 이벤트 프레임워크를 제공합니다. 이제 Flex Data Services는 클라이언트 mx.collections.ArrayCollection 인스턴스를 서버의 java.util.ArrayList로 매핑하고 서버측 java.util.Collection 구현은 클라이언트의 mx.collections.ArrayCollection으로 매핑합니다. 서버로부터의 응답에 원시 클라이언트 Array가 필요한 경우 원시 Java 배열(예: java.lang.Object[])을 반환할 수 있습니다. 또한 클라이언트 ActionScript Arrays는 서버에서 원시 java.lang.Object[] 배열로 역직렬화됩니다.

참고: mx.collections.ArrayCollectionflash.utils.IExternalizable을 구현합니다. 여기서 이 유형은 아직 HTTPChannel 클래스에 의해 완전히 지원되지 않습니다. 다른 유형과 IExternalizable 유형의 컨텐츠 사이에 객체 참조는 유지되지 않습니다.


출처 : http://www.adobe.com/kr/devnet/flex/articles/rpc_service.html

================================================
ニッシンのSEAFOOD NOODLEは おいしいですよ。
================================================
友達(ともだち)の勧(すす)めでインターネットで購入(こうにゅう )したラーメンが今日到着(とうちゃく)した。
ニッシンのSEAFOOD NOODLEだが、豚肉(ぶたにく)の汁(じゅう)が本当においしかった。
流通期限(りゅうつうきげん)が2週間くらい残って価格(かかく)も1000ウォン!もちろん、商品に問題はなかったよ。
10個(こ)かっだんですけど, もう5個食べ、半分(はんぶん)になって むねが痛いたみですよ。
今後(こんご)日本の他(た)のインスタント食品(しょくひん)を食べて見るべきだろう。
================================================
知らない 單語
================================================
勧(すす)め 권유, 권고
購入(こうにゅう) 구입
豚肉(ぶたにく) 돼지고기
汁(じゅう) 즙, 액체, 국물
流通(りゅうつう) 유통
期限(きげん) 기한
価格(かかく) 가격, 값
半分(はんぶん) 절반
むねが痛(いた)む 가슴이 아프다. 몹시 슬프다.
今後(こんご) 차후, 이후, 나중에
食品(しょくひん) 식품
べき  ~하는 것이 당연하다 ~하는 것이 올바르다
だろ-う  말하는 이의 추량·의문 등을 나타냄. …할 것이다. …하겠지.
================================================

'etc > old' 카테고리의 다른 글

객체 직렬화(Serializable)  (0) 2008.05.29
Flex Data Services 2에서 RPC 서비스 사용  (0) 2008.05.28
mysql에서 쿼리 사용방법  (0) 2008.05.28
문자열 형변환 예시  (0) 2008.05.27
携帯電話は必要悪(ひつようあく)だ  (0) 2008.05.27

+ Recent posts