Article Category

분류 전체보기 (202)
사는 이야기 (15)
Programming (174)
Photo (5)
게임 (2)
프로젝트 (0)

Recent Comment

Recent Trackback

Calendar

«   2019/08   »
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

Archive

  • Total96,717
  • Today3
  • Yesterday4
  1. 2008.08.06
    자바 개발자를 위한 Ajax: Ajax와 Direct Web Remoting
  2. 2008.08.06
    Apache Geronimo와 POJO로 SOA 프레임웍 구현하기

자바 개발자를 위한 Ajax: Ajax와 Direct Web Remoting

데이터 직렬화

 

 

 


 



난이도 : 중급

Philip McCarthy, Software development consultant, Independent

2006 년 10 월 17 일
2006 년 10 월 17 일 수정

Ajax 기능을 애플리케이션에 추가하기란 간단한 일이 아닙니다. 자바™ 개발자를 위한 Ajax 시리즈 세 번째 기사에서는 Direct Web Remoting (DWR)을 사용하여 JavaBeans 메소드를 JavaScript 코드에 직접 노출하고 Ajax를 자동화 하는 방법을 설명합니다.

Ajax 프로그래밍의 기초 (한글) 이해하는 것은 필수적인 일이지만, 복잡한 Ajax UI를 구현한다면, 고급 추상화 레벨에서 작업할 수 있어야 한다. Ajax for Java developers 시리즈 세 번째 글에서는 지난 달 소개했던 Ajax용 데이터 직렬화 기술 (한글)을 바탕으로, 자바 객체들을 직렬화 하는 문제를 단순화 하는 방법을 설명하겠다.

이전 글에서, JavaScript Object Notation (JSON)을 사용하여 클라이언트 상에서 JavaScript 객체로 쉽게 변환되는 포맷으로 데이터를 직렬화 하는 방법을 설명했다. 이 설정을 통해, JavaScript 코드를 사용하여 원격 서비스 호출을 호출하고, 원격 프로시저 호출과는 달리, 그에 대한 응답으로 JavaScript 객체 그래프를 받을 수 있다. 이번에는, 한 단계 더 나아가서 JavaScript 클라이언트 코드로부터, 서버 측 자바 객체에 대한 원격 프로시저 호출을 하는 기능을 규정하는 프레임웍을 사용해 보겠다.

DWR은 오픈 소스이며, 서버 측 자바 라이브러리, DWR 서블릿, JavaScript 라이브러리로 구성된 Apache 라이센스 솔루션이다. DWR이 자바 플랫폼에 사용할 수 있는 유일한 Ajax-RPC 툴킷은 아니지만, 가장 성숙하고, 많은 유용한 기능들을 제공하고 있다. 참고자료 섹션에서 DWR을 다운로드 하기 바란다.

DWR이란 무엇인가?

간단히 말해서, DWR은 서버 측 자바 객체의 메소드를 JavaScript 코드로 노출하는 엔진이다. DWR을 사용하여 애플리케이션 코드에서 Ajax 요청-응답 사이클 절차를 줄일 수 있다. 다시 말해서, 클라이언트 측 코드가 XMLHttpRequest 객체를 직접 다루거나 서버의 응답을 직접 다룰 필요가 없다는 것을 의미한다. 객체 직렬화 코드를 작성하거나 서드 파티 툴을 사용하여 객체를 XML로 전환할 필요가 없다. 서블릿 코드를 작성하여 Ajax 요청들을 자바 도메인 객체에 대한 호출로 중재할 필요도 없다.

DWR은 웹 애플리케이션에 서블릿으로서 전개된다. 블랙 박스처럼 보이는 이 서블릿은 두 가지 중요한 역할을 한다. 하나는, 각각 노출된 클래스에 대해, DWR은 JavaScript를 동적으로 생성하여 웹 페이지에 포함시킨다. 생성된 JavaScript에는 자바 클래스에 상응하는 메소드를 나타내는 스텁 함수가 포함되어 있고 막후에서 XMLHttpRequest도 수행한다. 이러한 요청들은 DWR 서블릿으로 보내지고, 요청들을 서버 측 자바 객체에 대한 메소드 호출로 변환하고, JavaScript로 인코딩 하여 메소드의 리턴 값을 다시 클라이언트로 보낸다. 이것이 두 번째 역할이다. DWR은 일반적인 UI 태스크를 수행하는 것을 돕는 JavaScript 유틸리티 함수도 제공한다.




위로


예제

DWR을 보다 자세히 설명하기 전에, 간단한 예제 시나리오를 소개하겠다. 이전 글에서와 마찬가지로, 온라인 스토어에 기반한 최소한의 모델을 사용하겠다. 이번에는 기본적인 제품 표현으로 구성된, 제품 아이템들을 포함시킬 수 있는 사용자의 쇼핑 카트와, 데이터 액세스 객체(DAO)를 사용하여 데이터 스토어에서 제품 상세를 검색한다. Item 클래스는 이전 글에서 사용했던 클래스이지만, 더 이상 수동 직렬화 메소드는 구현하지 않겠다. 그림 1은 설정 방법을 묘사한 것이다.


그림 1. Cart, CatalogDAO, Item 클래스를 나타내는 클래스 다이어그램

이 시나리오에서 두 개의 매우 간단한 유스 케이스를 설명하겠다. 첫 번째는 사용자가 카탈로그에서 텍스트 검색을 수행하고 매칭 아이템을 찾는 것이다. 두 번째는, 사용자가 아이템을 쇼핑 카트에 추가하고 카트에 있는 아이템들의 총 비용을 보는 것이다.




위로


카탈로그 구현하기

DWR 애플리케이션의 시작점은 서버 측 객체 모델을 작성하는 것이다. 이 경우, DAO를 작성하여 제품 카탈로그 데이터스토어에 검색 기능을 제공한다. CatalogDAO.java는 단순한 스테이트리스 클래스로서 인자 구조체가 없다. Listing 1은 Ajax 클라이언트로 노출 할 자바 메소드이다:


Listing 1. DWR을 통해 노출 할 CatalogDAO 메소드
				
/**
 * Returns a list of items in the catalog that have 
 *  names or descriptions matching the search expression
 * @param expression Text to search for in item names 
 *  and descriptions 
 * @return list of all matching items
 */
public List<Item> findItems(String expression);

/**
 * Returns the Item corresponding to a given Item ID
 * @param id The ID code of the item
 * @return the matching Item
 */
public Item getItem(String id);

다음에는, DWR을 설정하여, Ajax 클라이언트가 CatalogDAO를 구현하고 이러한 메소드를 호출하도록 할 것이다. Listing 2의 dwr.xml config 파일을 사용했다.


Listing 2. CatalogDAO 메소드를 노출하는 설정
				
<!DOCTYPE dwr PUBLIC
  "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
  "http://www.getahead.ltd.uk/dwr/dwr10.dtd">
<dwr>
  <allow>
    <create creator="new" javascript="catalog">
      <param name="class" 
        value="developerworks.ajax.store.CatalogDAO"/>
      <include method="getItem"/> 
      <include method="findItems"/> 
    </create> 
    <convert converter="bean" 
      match="developerworks.ajax.store.Item">
      <param name="include" 
        value="id,name,description,formattedPrice"/>
    </convert>
  </allow>
</dwr>

dwr.xml 문서의 루트 엘리먼트는 dwr이다. 이 엘리먼트 안에는 allow 엘리먼트가 있는데, 이것은 DWR이 원격 조정할 클래스들을 지정한다. allow의 두 개의 자식 엘리먼트들은 createconvert이다.

Creat 엘리먼트

Create 엘리먼트는 DWR에게 서버 측 클래스가 Ajax 요청으로 노출되도록 명령하고, DWR이 그 클래스의 인스턴스를 얻는 방법을 정의한다. creator 애트리뷰트에는 new 값이 설정되고, 이는 DWR이 클래스의 디폴트 컨스트럭터를 호출하여 인스턴스를 얻어야 한다는 것을 의미한다. 다른 기능들은 Bean Scripting Framework (BSF)를 사용하여 스크립트 조각을 통해 인스턴스를 만들거나, IOC 컨테이너인 Spring과의 통합을 통해 인스턴스를 얻는 것이다. 기본적으로, DWR에 대한 Ajax 요청이 creator를 호출하면, 인스턴스화 된 객체가 페이지 범위에 놓이고, 요청이 완료된 후에는 더 이상 사용할 수 없다. 스테이트리스 CatalogDAO의 경우, 이것은 괜찮다.

Createjavascript 애트리뷰트는 JavaScript 코드에서 액세스 될 객체 이름을 지정한다. create 엘리먼트 내에 중첩된 param 엘리먼트는 creator가 만들 자바 클래스를 지정한다. 마지막으로, include 엘리먼트는 노출되어야 하는 메소드의 이름을 지정한다. 노출될 메소드를 명확히 드러내는 것은 잠재적으로 발생할 수 있는 위험들을 피하기에 좋다. 엘리먼트가 생략된다면 모든 클래스의 메소드들은 원격 호출에 노출될 것이다. exclude 엘리먼트를 사용하여 접근을 방지하고 싶은 메소드만 지정할 수 있다.

Convert 엘리먼트

creator가 클래스를 노출하는 것과, 웹 리모팅 방식과 연관이 있는 반면, convertor는 그러한 메소드의 매개변수와 리턴 유형과 연관이 있다. convert 엘리먼트의 역할은 서버 측 자바 객체 구현과 직렬화된 JavaScript 구현 사이에서 데이터 유형을 변환하는 방법을 DWR에게 명령하는 것이다.

DWR은 자바와 JavaScript 구현들 간 데이터 유형들을 자동으로 중재한다. 이 유형에는 자바 프리머티브와 각각의 클래스 구현, String과 Date, 어레이, 컬렉션 유형들이 포함된다. DWR은 또한 JavaBeans를 JavaScript 구현으로 변환하고, 보안 때문에, 명확한 설정이 필요하다.

Listing 2convert 엘리먼트는 DWR에게 리플렉션 기반 빈 convertor를 CatalogDAO의 노출된 메소드에 의해 리턴된 Item에 사용하고 직렬화에 포함될 Item의 멤버를 지정한다. 멤버들은 JavaBean 네이밍 규약을 사용하여 지정되어, DWR은 상응하는 get 메소드를 호출할 것이다. 이 경우, 숫자 price 필드를 생략하고, 대신 통화로 포맷된 formattedPrice 필드를 포함시켰다.

이 시점에서, dwr.xml을 나의 웹 애플리케이션의 WEB-INF 디렉토리에 전개할 준비가 되고, 이 곳에서 DWR 서블릿이 집어낸다. 진행하기 전에, 모든 것이 생각한 대로 잘 작동하는지를 확인해보는 것이 좋다.




위로


전개 테스팅

DWRServletweb.xml 정의에서 init-param debugtrue로 설정했다면 DWR의 가장 유용한 테스트 모드가 실행된 것이다. /{your-web-app}/dwr/을 검색하면 DWR이 설정했던 클래스 리스트가 나타날 것이다. 해당 클래스에 대해 상태 스크린을 클릭한다. CatalogDAO용 DWR 테스트 페이지는 그림 2에 나타나 있다. script 태그를 웹 페이지에 붙일 뿐만 아니라, 클래스용으로 생성된 DWR의 JavaScript를 가리키면서, 이 스크린 역시 클래스의 메소드 리스트를 제공한다. 이 리스트에는 클래스의 상위 유형에서 상속된 메소드가 포함되어 있고, dwr.xml에서 원격용으로 명확하게 지정한 메소드도 액세스 가능한 것으로 표시된다.


그림 2. CatalogDAO용 DWR 테스트 페이지
The diagnostic and test page generated by DWR for CatalogDAO

매개변수 값을 액세스 가능한 메소드 옆 텍스트 박스에 입력하고 Execute 버튼을 눌러 이들을 호출할 수 있다. 단순한 값이 아니라면, 서버의 응답은 경고 박스에 JSON 공지를 사용하여 디스플레이 될 것이다. 이 경우 메소드를 따라서 한 줄로 디스플레이 된다. 이러한 테스트 페이지들은 매우 유용하다. 어떤 클래스와 메소드가 원격으로 노출되어있는지 쉽게 검사할 수 있고 각 메소드가 기대한 대로 작동하는지를 테스트 할 수 있다.

일단 원격 메소드가 올바르게 실행되었다면 DWR의 JavaScript 스텁을 사용하여 클라이언트 측 코드에서 서버 측 객체들을 호출할 수 있다.




위로


원격 객체 호출하기

원격 자바 객체 메소드와 이에 상응하는 JavaScript 스텁 함수들간 매핑은 간단하다. 일반적인 폼은 JavaScriptName.methodName(methodParams ..., callBack)인데, 여기에서 JavaScriptNamecreatorjavascript 애트리뷰트로 지정된 이름이고, methodParams는 자바 메소드의 n 매개변수이며, callback은 자바 메소드의 리턴 값과 함께 호출 될 JavaScript 함수이다. Ajax를 잘 알고 있다면 이 콜백 메커니즘이 XMLHttpRequest의 비동기화에 유용한 접근 방식이라는 것을 알 수 있을 것이다.

예제 시나리오에서, Listing 3의 JavaScript 함수를 사용하여 검색 결과로 UI를 검색 및 업데이트 한다. 이 리스팅은 DWR의 util.js에서 편리한 함수를 사용한다. $()라는 JavaScript 함수에 주목하라. 이것은 document.getElementById()의 다른 버전이라고 생각하면 된다. 타이핑 하기 쉽다. 프로토타입 JavaScript 라이브러리를 사용했다면, 이 함수가 익숙할 것이다.


Listing 3. 클라이언트에서 원격 findItems() 메소드 호출하기
				
/*
 * Handles submission of the search form
 */
function searchFormSubmitHandler() {

  // Obtain the search expression from the search field
  var searchexp = $("searchbox").value;

  // Call remoted DAO method, and specify callback function
  catalog.findItems(searchexp, displayItems);

  // Return false to suppress form submission
  return false;
}
       
/*
 * Displays a list of catalog items
 */
function displayItems(items) {

  // Remove the currently displayed search results
  DWRUtil.removeAllRows("items");

  if (items.length == 0) {
    alert("No matching products found");
    $("catalog").style.visibility = "hidden";
  } else {

    DWRUtil.addRows("items",items,cellFunctions);
    $("catalog").style.visibility = "visible";
  }
}

searchFormSubmitHandler() 함수에서, 재미있는 코드는 무엇보다도 catalog.findItems(searchexp, displayItems);이다. 이 한 줄의 코드는 네트워크를 통해 XMLHttpRequest를 DWR 서블릿으로 보내고 원격 객체의 응답과 함께 displayItems() 함수를 호출할 때 필요하다.

displayItems() 콜백 그 자체는 Item의 어레이와 함께 호출된다. 이 어레이는 DWRUtil.addRows() 함수로 전달되고, 전개할 테이블의 아이디와 함수의 어레이가 함께 전달된다. 각 테이블 행(row)에는 셀들이 있기 때문에 이 어레이에는 많은 함수들이 있다. 각 함수는 어레이에서 Item과 함께 호출되고 상응하는 셀에 전개될 콘텐트를 리턴해야 한다.

이 경우, 이 아이템 테이블의 각 행이 아이템의 이름, 디스크립션, 가격은 물론, 마지막 칼럼에는 아이템용 Add to Cart 버튼을 디스플레이 하도록 해야 한다. Listing 4는 이를 수행하는 셀 함수 어레이 모습이다.


Listing 4. 아이템 테이블을 전개하는 셀 함수 어레이
				
/*
 * Array of functions to populate a row of the items table
 * using DWRUtil's addRows function
 */
var cellFunctions = [
  function(item) { return item.name; },
  function(item) { return item.description; },
  function(item) { return item.formattedPrice; },
  function(item) {
    var btn = document.createElement("button");
    btn.innerHTML = "Add to cart";
    btn.itemId = item.id;
    btn.onclick = addToCartButtonHandler;
    return btn;
  }
];

처음 세 개의 함수들은 dwr.xml의 Itemconvertor에 포함된 필드의 콘텐트를 리턴한다. 마지막 함수는 버튼을 만들고, Item의 아이디를 여기에 붙이고, addToCartButtonHandler 함수가 버튼이 클릭될 때 호출되도록 지정한다. 이 함수는 두 번째 유스 케이스에 대한 엔트리 포인트이다. Item을 쇼핑 카트에 추가한다.




위로


쇼핑 카트 구현하기

DWR의 보안

DWR은 보안도 염두 해 두었다. dwr.xml을 사용하여 원격화 할 클래스와 메소드만 리스팅하면 악용될 수 있는 기능의 노출을 피할 수 있다. 게다가, 디버그 Test Mode를 사용하면 웹에 노출된 모든 클래스와 메소드를 검사할 수 있다.

DWR은 또한 역할 기반 보안을 지원한다. creator 설정을 통해 특정 빈에 액세스 하기 위해 사용자가 갖춰야 하는 J2EE 역할을 지정할 수 있다. URL 보안이 된 DWRServlet 인스턴스와 dwr.xml config 파일을 전개하여 다양한 사용자들에게 다양한 원격 기능들을 제공할 수 있다.

사용자 쇼핑 카트의 자바 구현은 Map에 기반하고 있다. Item이 카트에 추가될 때, Item은 키로서 Map으로 삽입된다. 그 Map에서 상응하는 값은 카트에 있는 Item의 양을 나타내는 Integer이다. 따라서 Cart.javaMap<Item,Integer>로 선언된 contents라는 필드를 갖고 있다.

해시(hash) 키로서 복합 유형을 사용하면 DWR에 문제가 생긴다. JavaScript에서, 어레이 키는 리터럴이어야 한다. 결과적으로, contents Map은 DWR에 의해 그 자체로 변환될 수 없다. 하지만, 쇼핑 카트 UI의 목적 상, 모든 사용자는 카트에 있는 각 아이템의 이름과 양을 봐야 한다. 따라서 getSimpleContents() 메소드를 Cart에 추가하여, contents Map을 취해 단순화된 Map<String,Integer>를 구현하여, 각 Item의 이름과 양만 나타내도록 하였다. 이러한 스트링 키 map 구현은 DWR의 빌트인 컨버터에 의해서 JavaScript로 변환될 수 있다.

클라이언트가 관심을 갖고 있는 Cart의 다른 필드는 totalPrice이다. 이것은 쇼핑 카트에 있는 모든 것의 총합을 나타낸다. Item과 마찬가지로, 숫자로 된 총합을 사전 포맷 된 String으로 나타낸 formattedTotalPrice을 제공했다.

카트 변환하기

콘텐트를 얻기 위해서 그리고 최종 가격을 알기 위해, 클라이언트 코드가 Cart로 두 번의 호출을 하도록 하는 대신, 이 모든 데이터를 클라이언트로 한번에 보낸다. 이를 위해서, 이상하게 보이는 메소드를 추가했다. (Listing 5):


Listing 5. Cart.getCart() 메소드
				
/**
 * Returns the cart itself - for DWR
 * @return the cart
 */ 
public Cart getCart() {
  return this;
}

이 메소드는 정상적인 자바 코드에서는 과잉이지만(메소드를 호출하면 Cart에 대한 참조가 이미 존재한다.) DWR 클라이언트가 Cart가 스스로를 JavaScript로 직렬화 시킬 수 있도록 한다.

getCart() 외에, 원격화 될 다른 메소드는 addItemToCart()이다. 이 메소드는 카탈로그 아이템의 아이디의 String 구현을 취해서, 이 아이템을 Cart에 추가하고, 총 합을 업데이트 한다. 이 메소드는 또한 Cart를 리턴하여, 클라이언트 코드가 Cart 콘텐트를 업데이트 하고 하나의 연산으로 새로운 상태를 받을 수 있다.

Listing 6은 확장된 dwr.xml config 파일로서, Cart 클래스를 원격화 하는데 필요한 여분의 config 정보가 포함되어 있다:


Listing 6. Cart 클래스를 통합하는 변경된 dwr.xml
				
<!DOCTYPE dwr PUBLIC
    "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
    "http://www.getahead.ltd.uk/dwr/dwr10.dtd">
<dwr>
  <allow>
    <create creator="new" javascript="catalog">
      <param name="class" 
        value="developerworks.ajax.store.CatalogDAO"/>
      <include method="getItem"/>
      <include method="findItems"/>
    </create>
    <convert converter="bean" 
      match="developerworks.ajax.store.Item">
      <param name="include" 
        value="id,name,description,formattedPrice"/>
    </convert>
    <create creator="new" scope="session" javascript="Cart">
      <param name="class" 
        value="developerworks.ajax.store.Cart"/>
      <include method="addItemToCart"/>
      <include method="getCart"/>
    </create>
    <convert converter="bean" 
      match="developerworks.ajax.store.Cart">
      <param name="include" 
        value="simpleContents,formattedTotalPrice"/>
    </convert>
  </allow>
</dwr>

이 버전의 dwr.xml에서, Cartcreatorconvertor를 추가했다. create 엘리먼트는 addItemToCart()getCart() 메소드가 원격화 되고, 생성된 Cart 인스턴트가 사용자 세션에 놓이도록 지정한다. 결과적으로, 카트의 콘텐트는 사용자 요청 사이에 되풀이 된다.

Cartconvert 엘리먼트는 원격 Cart 메소드가 Cart 자체를 리턴하기 때문에 필요하다. 직렬화 된 JavaScript에 나타나야 하는 Cart의 멤버는 simpleContents 맵과 formattedTotalPrice 스트링이다.

약간 혼란스럽다면, create 엘리먼트는 DWR 클라이언트에 의해 호출될 수 있는 Cart에 대한 서버 측 메소드를 지정하고, convert 엘리먼트는 Cart의 JavaScript 직렬화에 포함될 멤버를 지정한다는 것을 기억하면 된다.

이제, 원격 Cart 메소드를 호출하는 클라이언트 측 코드를 구현할 수 있다.




위로


원격 Cart 메소드 호출하기

무엇보다도, 스토어 웹 페이지가 처음 로딩되면, 세션에 저장된 Cart의 상태를 검사하고 싶다. 사용자가 아이템들을 Cart에 추가하고 페이지를 리프레쉬 하거나 다시 검색할 수 있기 때문에 이는 필요하다. 이러한 상황에서, 재 로딩된 페이지는 세션에서 Cart 데이터와 연결되어야 한다. 나는 이 페이지의 함수에 수행된 호출을 사용했다: Cart.getCart(displayCart). displayCart()는 서버에서 Cart 응답 데이터와 함께 호출된 콜백 함수이다.

Cart가 이미 세션에 있다면 creator는 이를 가져오고 이것의 getCart() 메소드를 호출할 것이다. 어떤 Cart도 세션에 없다면 creator는 새로운 것을 인스턴스화 하여, 이것을 세션에 두고, getCart() 메소드를 호출한다.

Listing 7은 아이템의 Add to Cart 버튼이 클릭될 때 호출되는 addToCartButtonHandler() 함수의 구현 모습이다:


Listing 7. addToCartButtonHandler() 구현
				
/*
 * Handles a click on an Item's "Add to Cart" button
 */
function addToCartButtonHandler() {

  // 'this' is the button that was clicked.
  // Obtain the item ID that was set on it, and
  // add to the cart.
  Cart.addItemToCart(this.itemId,displayCart);
}

모든 통신을 책임지고 있는 DWR을 사용하여 클라이언트에서의 카트에 추가하기 작동은 문자 그대로 한 줄의 함수이다. Listing 8은 최종 가격이다. Cart의 상태로 UI를 업데이트 하는 displayCart() 콜백의 구현이다:


Listing 8. displayCart() 구현
				
/*
 * Displays the contents of the user's shopping cart
 */
function displayCart(cart) {

  // Clear existing content of cart UI
  var contentsUL = $("contents");
  contentsUL.innerHTML="";

  // Loop over cart items
  for (var item in cart.simpleContents) {

    // Add a list element with the name and quantity of item
    var li = document.createElement("li");
    li.appendChild(document.createTextNode(
                    cart.simpleContents[item] + " x " + item
                  ));
    contentsUL.appendChild(li);
  }

  // Update cart total
  var totalSpan = $("totalprice");
  totalSpan.innerHTML = cart.formattedTotalPrice;
}

simpleContentsString을 숫자로 매핑하는 JavaScript 어레이라는 것을 기억하라. 각 스트링은 아이템의 이름이고, 연관 어레이의 상응하는 숫자는 카트에 있는 아이템의 수량이다. 따라서 cart.simpleContents[item] + " x " + item 식은 "2 x Oolong 128MB CF Card"로 계산된다.

DWR Store 애플리케이션

그림 3은 DWR 기반의 Ajax 애플리케이션 실행 모습이다. 오른쪽에 사용자 쇼핑 카트와 검색된 아이템들을 디스플레이 하고 있다:


그림 3. DWR 기반의 Ajax 스토어 애플리케이션 실행 모습
Screenshot of example scenario, with search results and shopping cart



위로


DWR의 장단점

함수의 일괄처리

DWR에서, 여러 원격 호출이 하나의 HTTP 요청과 함께 서버로 보내질 수 있다. DWREngine.beginBatch()를 호출하면 DWR에게 후속 원격 호출들을 바로 보내지 말라고 명령하는 것이다. 이들을 하나의 일괄 요청으로 묶지 않는다. DWREngine.endBatch()을 호출하면, 일괄 요청이 서버로 보내진다. 원격 호출은 서버 측에서 순서대로 실행되고, 각 JavaScript 콜백이 실행된다.

일괄 작업은 두 가지 방식으로 레이턴시를 줄일 수 있다. 우선, XMLHttpRequest 객체를 만들고, 각 호출에 대해 HTTP 연결을 만드는 오버헤드를 피할 수 있다. 또한, 실행 환경에서, 웹 서버는 많은 동시 HTTP 요청들을 다룰 필요가 없기 때문에 응답 시간이 빨라진다.

DWR을 사용하여 Ajax 애플리케이션을 구현하기가 얼마나 쉬운지를 배웠다. 이 샘플 시나리오는 단순하고 유스 케이스를 구현하는데 매우 단순한 접근 방식을 취했지만, DWR의 역할을 과소 평가해서는 안된다. 이전 글에서, Ajax 요청과 응답을 직접 설정하고 자바 객체 그래프를 JSON으로 변환하는 과정을 설명했지만, 여기에서는 DWR이 이 모든 작업을 수행했다. 나는 50줄 미만의 JavaScript를 작성하여 클라이언트를 구현했고, 서버 측에서도 내가 했던 일은, 나의 JavaBean에 두 개의 추가 메소드만 추가했을 뿐이다.

물론, 모든 기술에는 단점도 있기 마련이다. RPC 메커니즘과 마찬가지로, DWR에서도 원격 객체로 하는 호출이 로컬 함수 호출보다 훨씬 더 비싸다는 것을 쉽게 잊는다. DWR은 Ajax를 숨기는 일은 잘 하지만, 네트워크는 투명하지 않다는 것을 기억해야 한다. DWR 호출에는 레이턴시가 있고, 애플리케이션은 대단위 원격 메소드가 되도록 설계되어야 한다. 이러한 이유로 addItemToCart()Cart 자체를 리턴한다. addItemToCart()를 유효 메소드로 만드는 것이 더 자연스럽지만, 각 DWR 호출 다음에는 getCart()가 호출되어 변경된 Cart 상태를 검색한다.

DWR은 호출 일괄처리 시 레이턴시 문제에 대한 자체 해결책을 갖고 있다. (함수 일괄처리 사이드 바 참조) 애플리케이션에 맞는 대단위 Ajax 인터페이스를 줄 수 없다면 호출 일괄처리를 사용하여 여러 원격 호출들을 하나의 HTTP 요청으로 묶는다.

영역의 분리

본질상, DWR은 클라이언트 측과 서버 측 코드간 강결합을 만든다. 우선, 원격 메소드의 API 변경은 DWR 스텁을 호출하는 JavaScript로 반영되어야 한다. 두 번째(더 중요한 것은), 이 커플링이 클라이언트 측 영역이 서버 측 코드로 누수 되어야 한다. 예를 들어, 모든 자바 유형들이 JavaScript로 변환될 수 있는 것은 아니기 때문에 자바 객체에 추가 메소드를 추가하여 보다 쉽게 원격화 될 수 있도록 한다. 예제 시나리오에서, CartgetSimpleContents() 메소드를 추가하여 이 문제를 해결했다. 또한 getCart() 메소드를 추가했는데, 이것은 DWR 시나리오에서는 유용하지만 완전한 과잉이다. 원격 객체에 대한 대단위 API가 필요하고, 특정 자바 유형을 JavaScript로 변환하는 문제 때문에, 원격화된 JavaBean이 Ajax 클라이언트에만 유용한 메소드로 인해 어떻게 오염되는지를 볼 수 있다.

이를 해결하기 위해, 래퍼 클래스를 사용하여 여분의 DWR 스팩의 메소드를 JavaBean에 추가한다. JavaBean 클래스의 자바 클라이언트는 원격화와 관련된 과잉이 더 이상 없고, 원격화된 메소드에 보다 친숙한 이름을 줄 수 있다. 예를 들어, getFormattedPrice() 대신 getPrice()로 한다. 그림 4는 Cart를 래핑하여 여분의 DWR 기능을 추가하는 RemoteCart 클래스 모습이다:


그림 4. 원격 기능을 위해 Cart를 래핑하는 RemoteCart
Class diagram of RemoteCart wrapper class

마지막으로, DWR Ajax 호출은 비동기식이고, 이들이 파견된 순서대로 리턴 되리라고 기대해서는 안된다. 예제 코드에서 작은 문제를 무시했지만, 이 시리즈의 첫 번째 글에서는 순서 없이 도착하는 데이터에 대한 방어책으로서 응답에 타임스탬프를 실행하는 방법을 설명했다.




위로


맺음말

DWR은 많은 기능을 한다. 서버 측 도메인 객체에 대한 인터페이스를 빠르고 간단하게 만든다. 서블릿 코드, 객체 직렬화 코드, 클라이언트-측 XMLHttpRequest 코드를 작성할 필요가 없다. DWR을 사용하면 웹 애플리케이션으로의 전개도 매우 간단하고, DWR의 보안 기능은 J2EE 역할 기반 인증 시스템과 통합될 수 있다. DWR이 모든 애플리케이션 아키텍처에 적용되는 것은 아니지만 도메인 객체의 API 디자인에 고려해볼 만한 가치가 있다.

Ajax와 DWR의 장단점에 대해 보다 자세히 알고 싶다면 직접 다운로드 하여 시험해 보기 바란다. 여기에서 미처 다루지 못한 DWR의 많은 기능들이 있지만, 이 글에 소개된 소스 코드는 DWR을 시작할 수 있는 좋은 출발점이 된다. 참고자료 섹션에는 Ajax, DWR, 관련 기술들이 보다 자세하게 설명되어 있다.

가장 중요한 포인트는 Ajax 애플리케이션에는 정해진 하나의 솔루션이 없다는 것이다. Ajax는 새로운 기술들을 사용하는 개발 분야이다. 이 시리즈를 통해서 Ajax 애플리케이션의 웹 티어에서 자바 기술을 활용하는 방법에 초점을 맞췄다. XMLHttpRequest-기반 방식에 객체 직렬화 프레임웍을 선택하든, 아니면 DWR의 고급 추상화를 사용했든 상관은 없다. 다음 달, Ajax for Java developers 시리즈를 기대해주기 바란다.

기사의 원문보기





위로


다운로드 하십시오

설명 이름 크기 다운로드 방식
DWR source code j-ajax3dwr.zip 301 KB  FTP
다운로드 방식에 대한 정보


참고자료

교육

제품 및 기술 얻기

토론


필자소개

Philip McCarthy는 자바와 웹 기술 전문 소프트웨어 개발 컨설턴트이다. 현재는 Hewlett Packard 연구소와 Orange에서 디지털 미디어 및 텔레콤 프로젝트에 참여하고 있으며, City of London에서 금융 소프트웨어 관련 작업을 하고 있다. philmccarthy@gmail.com

Trackback 0 and Comment 0

Apache Geronimo와 POJO로 SOA 프레임웍 구현하기 (한글)

POJO 프로그래밍, GBeans, Geronimo 커널로 작업을 단순화 하기

 

 

 


 




난이도 : 중급

J. Jeffrey Hanson, Chief Architect, eReinsure.com, Inc.

2006 년 9 월 04 일
2006 년 10 월 23 일 수정

라이브러리와 프레임웍 때문에 생기는 애플리케이션 프로그램 인터페이스(API) 제약에 구애 받지 않고 소프트웨어를 개발할 수 있다는 것은 매력적인 제안입니다. 이러한 매력 때문인지 많은 사람들이 Plain Old Java™ Object (POJO) 프로그래밍을 채택하게 되었습니다. POJO의 개념은 쓸데없는 인터페이스나 서드-파티 API를 사용하지 않고 자바 플랫폼에서 소프트웨어를 개발하는 것입니다. Apache Geronimo 프레임웍은 POJO 개발의 확실한 토대가 되어 고급 애플리케이션과 서비스를 구현할 수 있습니다. Geronimo의 컴포넌트와 기술에 대해 알아봅시다.

서비스 지향 아키텍처(SOA)서비스 지향 프로그래밍은 비즈니스 로직을 모듈식 서비스로 캡슐화 하는 소프트웨어 엔지니어링 스타일을 칭하는 용어이다. 이 서비스는, 서비스 공급자와 서비스 소비자 간 제휴가 약한(loosely-coupled) 동적인 런타임 환경을 주 타겟으로 한다. 약결합 상태의 서비스들은 컴파일 시 제휴되지 않으므로 런타임 시 동적으로 연결할 수 있고, 개발자들은 필요에 따라 전개를 할 수 있다. 약결합(loose coupling)외에도 서비스 지향 환경의 몇 가지 특성을 요약하면 다음과 같다.

  • 대단위(Coarse granularity): 서비스와 관련된 세분성이라 함은 서비스를 공개적으로 표현하는 기능과 관련이 있다. 소단위(Fine-grained) 서비스들은 특정 수준의 기능을 정의하는 공식 인터페이스를 말한다. 대단위 서비스(grained services)들은 해당 비즈니스 영역에 맞는 보다 일반적인 수준의 기능을 나타낸다.
  • 위치 투명성(Location transparency): 서비스가 네트워크의 어느 곳에 있든지 클라이언트는 서비스에 액세스 할 수 있다.
  • 프로토콜 독립(Protocol independence): 통신/네트워크 프로토콜과 관계 없이 클라이언트는 서비스에 액세스 할 수 있다.

이러한 개념들을 모두 적용하여 서비스를 구현하는 일은 어려운 일이 아닐 수 없다. POJO 프로그램이 이 태스크를 단순화 시킬 수 있다.

POJO

POJO는 특정한 외부 인터페이스나 서드-파티 API에 의존하지 않는 자바 클래스이다. 따라서 코드를 외부적 제휴 상태와 분리할 수 있다. 디커플링(decoupling)의 주요 이점들 중 하나가 소프트웨어 개발자들에게 주어지는 자유이다. 지속성(persistence), 트랜잭션 지원, 리모팅(remoting)같은 가외 작업들로부터 개발자는 해방된다. 많은 기술들이 컴포넌트/클래스 디커플링을 줄이고 POJO 프로그래밍을 활용하고 있다.

  • 주석(Annotations)은 리모팅, 지속성, 프레임웍 지원 같은 기능이나 특성을 지원하기 위해 클래스나 클래스의 일부를 "꾸미는" 코드를 생성하기 위해 개발 툴이 사용하는 메타데이터이다.
  • 의존성 삽입(Dependency injection)플러거블(pluggable) 컴포넌트를 구현하는 기술로서, 이것으로 객체 생성과 제휴가 컴포넌트가 아닌, 컨테이너나 어셈블러 컴포넌트에 의해 수행된다.
  • 리플렉션(Reflection)은 주어진 클래스나 인터페이스에 대해 메소드, 필드, 컨스트럭터 같은 정보의 발견이다.

각각의 디커플링 기술은 장단점이 있다. 이 글에서는 리플렉션과 Geronimo의 GBean 의존성 삽입을 사용하는 POJO 프로그래밍을 사용하는 SOA 프레임웍에 대해 설명하겠다.




위로


JMX와 Geronimo

Geronimo는 Java Management Extensions (JMX)와 GBeans이라고 하는 관리 컴포넌트의 의존성 삽입 프레임웍을 사용하는 범용 커널에 구현된다. Geronimo의 가상의 모든 것들(어댑터, 애플리케이션, 컨테이너)은 GBean이거나, GBean에 기반을 두고 있다. GBean은 JMX와 JMX Managed Beans (MBeans)과 유사하며 인프라스트럭처도 공유하고 있다.

JMX

JMX는 시스템 관리, 애플리케이션 관리, 리소스 관리를 위한 자바 표준으로 부각되고 있다. JMX는 동적으로 늘어나는 자바 클래스, 인터페이스, 그리고 애트리뷰트와 연산을 가진 런타임 객체용 표준을 정의한다. 이러한 확대(augmentation) 기술은 인스트루먼테이션(instrumentation)이라고도 한다.

JMX는 자바 프로그래밍 언어를 사용하여 추상화 할 수 있는 어떤 리소스(애플리케이션, 장치, 서비스)라도 관리할 수 있다. 관리를 받는 각 리소스를 MBean이라고 한다. JMX는 네 가지 유형의 MBean을 정의한다:

  • Standard MBeans 자바 인터페이스로 관리 애트리뷰트와 연산을 정의한다.
  • Dynamic MBeans 런타임 발견을 통해 관리 애트리뷰트와 연산을 정의한다.
  • Model MBeans 관리가 가능한 연산과 애트리뷰트를 노출하기 원하는 객체용 프록시로서 작동한다.
  • Open MBeans 사전에 정의된 메타데이터 어휘를 사용하여 클래스와 객체들의 애트리뷰트와 연산을 노출한다.

MBean과 인터랙팅 하는 주 인터페이스는 javax.management.MBeanServer이다. MBeanServer는 MBean의 중앙 리파지토리로서 작동하고 MBean 클라이언트에서 MBean과의 통신이 수월해진다.

MBean은 ObjectName 객체에 의해 구분된다. ObjectName 인스턴스 구성 요소는 다음과 같다:

  • 도메인, 해당 도메인의 이름이다. 자바 패키지 네이밍과 같은 방식으로 리버스 도메인 네임 시스템(DNS) 네이밍을 사용하는 것을 권장한다.
  • 키 속성 리스트, 임의의, 무작위 키와 관련 값이다.

다음 코드는 전형적인 ObjectName 객체를 만드는 방법이다:

String domain = "com.jeffhanson.test";
String keyPropertyList =
"domain:Name=TestBean,Type=GenericService";
ObjectName objName =
new ObjectName(domain + ":" + keyPropertyList);


ObjectName 객체는 많은 MBeanServer 메소드에 대한 매개변수로서 사용되어 애트리뷰트를 가져오고 MBean에 대한 연산을 실행한다.

Geronimo의 GBean 프레임웍

GBean은 Geronimo에서 관리되는 컴포넌트이다. JMX MBean과 유사점도 많고 관련도 많다. GBeanInfo라는 클래스에 따라 애트리뷰트와 연산을 노출한다. 이는 JMX의 MBeanInfo 클래스와 매우 비슷하다. Geronimo는 MX4J 라이브러리를 사용한다. (Resources)

GBean은 상태와 제휴 의존성을 관리하고 라이프 사이클 이벤트를 핸들한다. GBean은 다른 GBean의 상태에 관련 당사자로서 등록한다. GBean이 시작한 후에 GBean은 의존성 삽입을 통해 해당 GBean에 대한 레퍼런스를 받는다. GBean은 주어진 시간에 이러한 7 개의 라이프 사이클 상태 중 하나가 된다:

  1. Loaded
  2. Unloaded
  3. Starting
  4. Running
  5. Stopping
  6. Stopped
  7. Failed

Listing 1은 애트리뷰트(메시지)를 포함하고 있는 간단한 GBean이다.



































Listing 1. 전형적인 Gbean
public class TestGBean

   implements GBeanLifecycle

{

   private static GBeanInfo GBEAN_INFO = null;



   static

   {

      GBeanInfoBuilder infoFactory =

         GBeanInfoBuilder.createStatic(TestGBean.class);

      infoFactory.addAttribute("message", String.class, true);

      infoFactory.addOperation("getMessage");

      GBEAN_INFO = infoFactory.getBeanInfo();

   }



   private String message;



   public String getMessage()

   {

      return message;

   }



   ...

}


Listing 2의 코드로 GBean을 시작(활성화) 및 중지할 수 있다.


Listing 2. GBean 시작하기
ObjectName testGBeanOName =

   ObjectName.newInstance("jeffhanson.test:ID=test");

GBeanData gBeanData =

   new GBeanData(testGBeanOName, TestBean.GBEAN_INFO);

gBeanData.setAttribute("message", "Hello world");

geronimoKernel.loadGBean(gBeanData,

                         Thread.currentThread().

                            getContextClassLoader());

geronimoKernel.startGBean(testGBeanOName);

...

geronimoKernel.stopGBean(testGBeanOName);

geronimoKernel.unloadGBean(testGBeanOName);


Geronimo 커널을 통해 GBean을 사용할 수 있다.




위로


Geronimo 커널

A Geronimo 커널은 GBean의 프레임웍이다. 이 프레임웍을 사용하여 어떤 고급 시스템이라도 GBean 컨테이너와 GBean 컴포넌트로서 모델링 및 구축하여 상태, 관계, 이벤트 핸들링을 관리할 수 있다.

Geronimo 커널을 프로그래밍 방식으로 구현할 때 KernelFactory 클래스를 사용하면 간단하다. Listing 3TestGeronimo라는 새로운 Geronimo 커널을 만들고, 커널을 부팅하고, 부팅 시 로깅하며 서블릿 GBean을 로딩 및 시작하는 방법을 설명하고 있다.


Listing 3. Geronimo 커널 만들기
try

{

   Kernel geronimoKernel =

      BasicKernelFactory.newInstance().

         createKernel("TestGeronimo");



   geronimoKernel.boot();

 

   log.debug("Geronimo BootTime: "

             + geronimoKernel.getBootTime());



   // add the servlet GBean

   ObjectName servletObjName =

      new ObjectName("jeffhanson.test:ID=MyGBean");

   GBeanData servletGBeanData = new GBeanData(servletObjName,

                                              GBEAN_INFO);

   ClassLoader classLoader = getClass().getClassLoader();

   geronimoKernel.loadGBean(servletGBeanData, classLoader);

   geronimoKernel.startGBean(servletObjName);

}

catch (Exception e)

{

   log.error(e);

}


커널을 만들고 실행한 후에 POJO 서비스에 대해 메소드를 실행할 때 Geronimo 커널 서버와 이것의 리플렉션 기능을 사용하면 쉬워진다. (Listing 4)


Listing 4. 커널과 함께 등록된 서비스 호출하기
private static

Object invokePOJOService(ObjectName serviceObjName,

                         String operationName,

                         String[] params)

   throws Exception

{

   String[] paramTypes = null;

   if (params != null && params.length > 0)

   {

      paramTypes = new String[params.length];

      for (int i = 0; i < params.length; i++)

      {

         paramTypes[i] = params[i].getClass().getName();

      }

   }



   Kernel geronimoKernel =

      KernelManager.getInstance().getGeronimoKernel();



   Object retVal = 

      geronimoKernel.invoke(serviceObjName,

                            operationName,

                            (Object[])params,

                            paramTypes);



   return retVal;

}





위로


Geronimo에서 서비스 지향 POJO의 프레임웍

POJO-for-SOA 프레임웍은 Geronimo 커널의 인스턴스를 사용하여 POJO를 GBean으로서 등록하여, 관련 클라이언트가 추가 인터페이스나 API 없이 이들을 호출 및 쿼리 할 수 있도록 한다. 이 프레임웍은 멀티 티어 엔터프라이즈 애플리케이션 환경에서 비즈니스 티어에 있다. service-locator 클래스는 이 커널과 인터랙팅 하여 POJO가 서비스로서 사용될 수 있도록 하는 책임을 맡고 있다. service-locator 클래스는 business-delegate 컴포넌트로 POJO를 리턴한다. 그림 1은 프레임웍 내의 컴포넌트들 간 관계도이다.


그림 1. POJO-for-SOA 프레임웍
POJO-for-SOA framework

이 프레임웍은 클라이언트에서 HTTP 요청을 받고 요청을 business-delegate 컴포넌트로 요청을 보내는 디스패처 컴포넌트를 통해 요청을 전달한다. business-delegate 컴포넌트는 service-locator를 사용하여 해당 요청에 대한 서비스를 찾는다. business-delegate 컴포넌트는 서비스를 호출하고 리턴 값을 모델 객체로 래핑한다. 해당 뷰 컴포넌트는 모델 객체를 처리하고 이것을 클라이언트에게 응답 포맷을 사용하여 보낸다. 그림 2는 시퀀스 다이어그램이다.


그림 2. 전형적인 HTTP 요청 및 서비스 호출 시퀀스
typical HTTP request and service invocation

그림 3의 클래스 다이어그램은 프레임웍의 클래스들 간 관계도이다.


그림 3. 프레임웍 클래스들 간 관계
Relationships between framework classes



위로


프레임웍 전개 및 실행

이 프레임웍은 엔터프라이즈 애플리케이션 시스템의 비즈니스 티어에 있다. 프레임웍은 HTTP 요청을 받고 내용을 프레임웍으로 보내는 하나의 서블릿을 노출한다. 다음 섹션에서는 간단한 전개 과정을 설명하겠다.

프레임웍 전개하기

.war 파일에 프레임웍용 클래스와 엔터프라이즈 애플리케이션을 패키징하여 이를 geronimo_home/deploy 디렉토리에 둔다. 디렉토리가 없다면 만들기 바란다.

Geronimo는 시작 시 .war 파일을 자동으로 전개한다. 전개 디렉토리에 있는 애플리케이션들이 로딩되고, Geronimo는 변경 사항이 생길 경우 런타임 시 애플리케이션을 다시 로딩한다. 따라서 애플리케이션 디버깅이 매우 편리하다.

프레임웍 테스트

geronimo_home/bin 디렉토리에 있는 시작 스크립트(startup.bat 또는 startup.sh)를 사용하여 Geronimo 애플리케이션 서버를 시작한다. Geronimo 시작 스크립트를 호출하면 Geronimo 콘솔 윈도우가 보인다. 프레임웍과 애플리케이션을 전개한 후에 Geronimo의 콘솔 윈도우에는 Listing 5와 비슷한 라인들이 생기면서 웹 애플리케이션이 성공적으로 시작되었다는 것을 확인한다.


Listing 5. 웹 애플리케이션이 성공적으로 시작되었다!
0 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel  -

Starting boot

422 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState

- GBeanInstanceState for: :role=Kernel State changed from stopped to

starting

422 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState

- GBeanInstanceState for: :role=Kernel State changed from starting to

running

422 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel  -

Booted

640 [main] DEBUG com.jeffhanson.apptier.FrontController  - Geronimo

BootTime: Sat May 20 18:51:08 MDT 2006

656 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState

- GBeanInstanceState for: jeffhanson.test:ID=FrontController State

changed from stopped to starting

656 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState

- GBeanInstanceState for: jeffhanson.test:ID=FrontController State

changed from starting to running


다음 URL을 웹 브라우저 윈도우에 입력하여 HelloWorld 서비스에 대해 setMessage 연산 수행한다:

http://<host>:<port>/<context>?Action=
HelloWorld&Operation=setMessage&Params=Hello+everybody!


프레임웍이 요청을 처리하면 콘솔 아웃풋은 다음과 같을 것이다. (Listing 6)


Listing 6. setMessage 연산 프로세싱 결과
719 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator  -

Adding service [HelloWorld] to kernel...

719 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator  -

Loading GBean: jeffhanson.test:Name=HelloWorld,Type=GenericService

734 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState

- GBeanInstanceState for:

jeffhanson.test:Name=HelloWorld,Type=GenericService State changed

from stopped to starting

734 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState

- GBeanInstanceState for:

jeffhanson.test:Name=HelloWorld,Type=GenericService State changed

from starting to running


다음 URL을 웹 브라우저 윈도우에 입력하고 HelloWorld 서비스에 sayHello 연산을 실행한다.

http://<host>:<port>/<context>?
Action=HelloWorld&Operation=sayHello


프레임웍이 요청을 처리할 때 콘솔 아웃풋은 다음과 같이 된다. (Listing 7)


Listing 7. sayHello 처리 결과
750 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator  -

serviceObjName: jeffhanson.test:Name=HelloWorld,Type=GenericService

750 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator  -

Service  [HelloWorld] already in kernel

1156 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator  -

serviceObjName: jeffhanson.test:Name=HelloWorld,Type=GenericService

1156 [main] INFO com.jeffhanson.businesstier.services.HelloWorld  -

Hello everybody!


서블릿 엔진이 서블릿을 중지하고 그 서블릿에 대해 destroy 메소드를 호출하면 서블릿은 Geronimo 커널을 중지한다. 서블릿 엔진이 서블릿을 중지하면 콘솔 아웃풋은 다음과 같다. (Listing 8)


Listing 8. 서블릿 중지 결과
1156 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel  -

Starting kernel shutdown

1156 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel  -

Kernel shutdown complete


HelloWorld 클래스는 setMessage메소드, getMessage 메소드, sayHelloWorld 메시지를 가진 간단한 POJO이다. 이 클래스의 인스턴스를 Geronimo 커널과 함께 등록한 후에 인스턴스를 호출하고, 의존성 삽입을 사용하여 이를 다른 서비스와 컴포넌트들과 런타임 시 제휴시킬 수 있다. Listing 9의 코드는 간단한 HelloWorld POJO 클래스를 설명하고 있다.


Listing 9. HelloWorld 서비스
package com.jeffhanson.businesstier.services;



import org.apache.log4j.Logger;



public class HelloWorld

{

   private static Logger log = Logger.getLogger(HelloWorld.class);



   private String message = "Hello world";



   public void setMessage(String message)

   {

      if (message == null || message.length() <= 0)

      {

         throw new RuntimeException("HelloWorld.setMessage "

                                    + "param is not set");

      }



      this.message = message;

   }



   public String getMessage()

   {

      return message;

   }



   public void sayHello()

   {

      log.info(message);

   }

}





위로


요약

변화하는 비즈니스와 이벤트에 시의 적절하게 대응할 수 있는 민첩하고 효과적인 SOA를 디자인 하기란 여간 어려운 일이 아니다. 하지만 올바르게 설계된 POJO 레이어를 중심으로 구현된 SOA라면 가능하다. Geronimo 플랫폼은 POJO를 사용하여 유연하고 확장성 있는 SOA를 구현하는데 쓰이는 프레임웍과 툴을 제공한다.

기사의 원문보기





위로


다운로드 하십시오

설명 이름 크기 다운로드 방식
SOA with POJOs framework GeronimoSOAwithPOJOs.zip 14KB HTTP
다운로드 방식에 대한 정보


참고자료

교육

제품 및 기술 얻기

토론


필자소개

Jeff Hanson

Jeff Hanson은 20년 이상을 소프트웨어 업계에 몸담았다. Microsoft® Windows®의 OpenDoc 프로젝트의 시니어 엔지니어였으며 Route 66 프레임웍의 아키텍트 리더였다. 현재 eReinsure.com, Inc.의 핵심 아키텍트로서 있으며 Java EE 기반의 보험 시스템용 웹 서비스 프레임웍과 플랫폼을 구현하고 있다. .NET versus J2EE Web Services: A Comparison of Approaches, Pro JMX: Java Management Extensions, Web Services Business Strategies and Architectures. 등을 집필했다.

Trackback 0 and Comment 0
prev Prev : [1] : [2] : [3] : [4] : Next next