달력

42024  이전 다음

  • 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

출처 : http://www.ihelpers.co.kr/webdevdoc/index.php

 

  웹개발 문서 모음

웹사이트 개발시에 필요한 문서 목록입니다.
해당 문서목록의 의견,문제점,궁금하신 점은 Q&A게시판에 올려주십시요.
공유를 원하는 유용한 웹개발문서를 받고 있습니다. 많이 많이 여기로 보내주세요.(다양한 업종의 스토리보드와 제안서 강추)

Q : 파일 다운로드시 한글파일의 경우 다운로드 되지 않거나 글자가 깨집니다.
A : 익스플로러 "도구 > 인터넷 옵션 > 고급 "에서 고급 UTF8옵션 해제하시면 됩니다.

* 버전 : v2.0
* 최종수정일자 : 2005.07.08 10:30:22

'JAVA > JSP_Servlet' 카테고리의 다른 글

jetty site  (0) 2005.10.12
[펌] myeclipse에서 xdoclet 설명  (0) 2005.10.04
[openlaszlo] 아래에 푸터 붙어있을때...  (0) 2005.07.05
jwebunit  (0) 2005.06.03
OSCache / Quartz ... 등등 좋은것들..  (0) 2005.06.03
Posted by tornado
|
Posted by tornado
|

[링크]SmartClient

JAVA/JEE 2005. 8. 10. 10:37
Posted by tornado
|
 Enterprise Java Technologies Tech Tip에 오신 여러분을 환영합니다
Enterprise Java Technologies
테크팁
2005년 7월 26일자
 
Java 2 Platform, Enterprise Edition (J2EE)에 기반한 enterprise Java technologies 와 APIs의 사용에 관한 최신 정보를 얻어 가시기 바랍니다.

이번 호에서는,

» JAX-RPC를 사용하여 간단한 웹서비스 구현하기
» JAXB로 RELAX NG 사용하기

를 다루게 됩니다.


이 글에서는 Java 2 Java 2, Enterprise Edition, v 1.4 를 사용합니다.
다운로드

저자 Robert Eckstein

JAX-RPC를 사용하여 간단한 웹서비스 구현하기
 

이미 웹 서비스와 자바 기술 대해 작성된 문서들이 많이 있다. 이 중 심도깊은 내용을 다루는 문서도 있었지만, 사람들은 여전히 자바 기술을 사용하여 웹 서비스를 생성하도록 도와주는 간결하고 직접적인 문서를 원한다. 이번 테크팁은 이를 위한 것이다. 간단한 웹 서비스를 구축하는 과정과 XML기반의 RPC(JAX-RPC)를 위해 Java API를 사용하여 서비스에 접근하는 클라이언트를 구축하는 과정에 대해 단계적으로 다룬다. 이 단계는 J2EE 1.4 tutorial에 있는 Chapter 8: Building Web Services with JAX-RPC를 기반으로 한다. 이들은 웹 서비스와 웹서비스의 클라이언트를 생성하는 기본적인 단계들이다.
  1. 서비스를 위한 위한 SEI(service endpoing interface)와 SEI의 실행 클래스 코드를 작성한다.
  2. SEI와 실행클래스를 컴파일한다.
  3. WSDL과 매핑 파일들을 생성한다.
  4. 서비스를 패키지화하고 배치한다. 특별한 동일 클래스들(클라이언트와의 커뮤니케이션에 사용된)은 서비스가 배치되는 동안에 응용서버에 의해 생성된다.
  5. 클라이언트 클래스 소스를 작성한다.
  6. Stub 파일들을 생성한다.
  7. 클라이언트 클래스들을 컴파일한다.
  8. 클라이언트를 패키지화한다.
SEI와 실행 클래스 코드 작성

JAX-RPC 웹 서비스를 개발하는 데 있어서 시작 포인트는 SEI(service endpoint interface)이다. 이것은 단순히 클라이언트가 그 서비스에 호출할 수 있는 방법을 선언하는 자바 인터페이스이다.

JAX-RPC를 통하여 클라이언트들에게 사용 가능한 웹서비스를 만들어 주기 위해 2개의 자바 클래스를 생성할 필요가 있다. 하나는 SEI를 정의하는 것, 그리고 다른 하나는 SEI 구현하는 것이다. 이 두 클래스들의 소스 코드는 이번 테크팁의 샘플 코드인 ttmay2005-ws.jar를 사용한다. 다음은 웹 서비스를 위한 SEI를 정의하는 클래스이다.
   package helloservice;   import java.rmi.Remote;   import java.rmi.RemoteException;   public interface HelloIF extends Remote {       public String sayHello(String s) throws RemoteException;   } 
SEI는 다음의 규칙을 따라야 한다.
  • java.rmi.Remote 인터페이스를 확장한다.
  • public / final / static과 같은 일정한 선언을 가져서는 안된다.
  • 메소드들은 java.rmi.RemoteException이나 그것의 하위 클래스들 중의 하나를 throw해야한다. (메소드들은 또한 service-specific exception들을 throw할 수 있다.)
  • 메소드 매개변수들과 리턴 타입들은 반드시 JAX-RPC타입들에 의해 지원되어야한다. (JAX-RPC 타입 목록를 위해)
다음은 실행 클래스의 코드이다. 이 클래스는 문자열 매개변수를 수용하며, 그 매개변수를 약간 수정된 문자열을 리턴하기 위해 사용한다.
   package helloservice;   public class HelloImpl implements HelloIF {       public String message = "Hello there, ";       public String sayHello(String s) {           return message + s;       }   } 
SEI 클래스와 실행 클래스 컴파일하기

SEI 클래스와 실행 클래스를 생성한 후에는 그들을 컴파일해야한다. 이를 위한 쉬운 방법은 ant 설정 도구(http://ant.apache.org/)를 사용하는 것이다. 웹 서비스를 위한 구축 파일(build.xml)은 이 팁의 샘플 코드에 있다. 샘플을 위한 구축 파일과 같은 디렉토리에서 ant를 실행하여 구축할 수 있다. 현재의 디렉토리를 그 샘플 코드를 설치한 ws/helloservice 밑으로 변경한다. 그리고 다음의 명령을 입력한다.
   ant 
빌드하는데 가장 먼저 이뤄지는 것은 웹서비스를 위한 디렉토리를 생성하는 것이다. 그런 다음 파일들을 컴파일한다. 컴파일 단계는 다음의 javac 명령어들을 실행하는 것과 동일하다.
   src\helloservice>javac -d ..\..\build -cp ..\   ..\build HelloIF.java      src\helloservice>javac -d ..\..\build -cp ..\..\   build HelloImpl.java
(참고: 각각의 명령은 이 문서상에서는 두 줄로 나타내지만 실제로는 한 라인에 입력해야 한다.)

WSDL과 매핑 파일들의 생성

WSDL 파일은 웹서비스를 표현한다. 클라이언트에게 그 서비스에 대한 요구를 하는 데 있어서 어떤 포맷을 이용해야 하는 지를 말해주는 것이 바로 WSDL 파일이다. Wscompile는 JAX-RPC SEI와 WSDL 파일 사이에 매핑을 제공하는 J2EE 1.4 SDK에 패키지된 툴이다. SEI로부터 WSDL 파일을 생성시키거나 WSDL 파일로부터 SEI를 생성시키기 위해 wscompile툴을 구동할 수 있다. 이 만약 이 팁을 위한 구조를 설정하기 위해 ant 명령어를 사용하게 되면, 그것은 wscompile 도구를 구동하여 웹서비스를 위한 WSDL 파일을 생성한다. 그 구조는 또한 mapping.xml 파일을 생성하는데, 매핑 파일은 웹서비스의 WSDL 정의를 자바 인터페이스에 매핑한다.

만약 ant로 빌드하지 않는다면 디렉토리의 안에 다음의 명령을 구동하여 mapping.xml과 WSDL 파일을 새로 만들 수 있다.
   wscompile -define -mapping build\mapping.xml -d build    -nd build -classpath build config-interface.xml
(참고: 이 문서상에서는 두 줄로 나타내지만 실제로는 한 라인에 입력해야 한다.)

wscompile 명령에 있는 플래그는 각각 다음과 같은 의미이다.
-classpath. build 디렉토리에서 SEI를 읽도록 wscompile 툴을 지시한다. -define. WSDL을 생성하도록 wscompile을 지시한다.-mapping. 매핑 파일을 생성하도록 wscompile를 지시한다.  -d. Build 하위 디렉토리에 클래스 파일을 쓰도록 wscompile를 지시한다. -nd. Build 하위 디렉토리에 WSDL 파일을 쓰도록 wscompile를 지시한다. 
wscompile 툴을 구동할 때, 또한 SEI에 대한 정보를 명시하는 wscompile 구성 파일을 제공해야 한다. 구성 파일은 이 테크팁의 샘플 코드로 제공되는데, 그 구성파일은 config-interface.xml이라 하며 다음의 내용을 포함하고 있다.
   <?xml version="1.0" encoding="UTF-8"?>   <configuration      xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">     <service          name="MyHelloService"          targetNamespace="urn:Foo"          typeNamespace="urn:Foo"          packageName="helloservice">         <interface name="helloservice.HelloIF/>     </service>   </configuration>
이 구성 파일은 wscompile에게 MyHelloService.wsdl라는 이름의 WSDL 파일을 생성하도록 명령한다. 그 파일은 또한 다음의 정보와 명령을 제공된다.
  • 서비스명은 MyHelloService이다.
  • SEI는 helloservice.HelloIF이다.
  • helloservice 패키지에 그 서비스 클래스를 놓는다.
  • WSDL 대상과 타입 네임 스페이스는 urn:Foo이다. 네임 스페이스로 무엇을 이용할 것인지는 사용자가 선택할 수 있다. 네임 스페이스의 역할은 자바 패키지 이름의 사용과 비슷하다.즉, 그렇지 않으면 충돌할 수 있는 이름을 식별하는 것이다. 예를 들어, 어떤 회사는 회사의 모든 자바 코드가 com.wombat.* 패키지 안에 있도록 결정할 수 있다. 마찬가지로, 또한 네임스페이스 http://wombat.com을 사용하기로 결정할 수도 있다.
서비스의 패키지와 배치

그 다음으로 서비스를 패키지화하고 배치시킬 필요가 있다. 이를 위해서는 배치 기술어에 서비스에 대한 세부사항을 명시할 필요가 있다. 웹 서비스는 서블릿이나 비상태유지 세션빈(관련된 서비스를 하는데 상태정보를 유지하지 않은 bean;stateless session bean)으로써 구현될 수 있다. 웹 서비스는 서블릿들이 Web Archive (WAR) 파일에 패키지됨에 따라 구현된다. WAR 파일에 있는 WEB-INF 디렉토리는 웹 어플리케이션(web.xml, sun-web.xml)과 특별한 웹 서비스 배치 기술어 파일(webservices.xml)을 위한 두 개의 표준 배치 기술어 파일을 포함한다. 웹 서비스는 Bean이 EJB-JAR 파일에 비상태유지 세션 빈이 패키지됨에 따라 구현되며, 배치 기술어 파일은 META-INF 디렉토리에 있다.

이번 테크팁을 빌드를 하기 위해 ant 명령을 사용한다면, 사용자를 위해 서비스를 패키지한다. 특히 이 빌드는,
  • WAR 내용을 모으기 위해 임시 디렉토리를 생성한다.
  • 임시 디렉토리에서 두 하위디렉토리(WEB-INF와 build)를 생성한다.
  • WEB-IN디렉토리에서 두 하위 디렉토리(클래스들과 wsdl)를 생성한다.
  • HelloIF.class와 HelloImpl.class를 WEB-INF/classes 디렉토리로 복사한다.
  • MyHelloService.wsdl을 WEB-INF/wsdl 디렉토리로 복사한다.
  • mapping.xml 파일을 그 구축 디렉토리로 복사한다.
  • 임시 디렉토리로부터 WAR 파일을 새로 만든다. WAR파일은 WEB-INF 디렉토리에서 web.xmlsun-web.xml 배치 기술어, webservices.xml 파일을 둔다.
다른 대안으로, J2EE 1.4 Application Server의 배치 툴 유틸리티을 사용하는 웹서비스를 패키지 할 수 있다.

WAR 파일이 새로 만들어진 후에는, 이를 deploytool이나 다른 어플리케이션을 사용하여 배치시킬 수 있다. 배치가 성공적이면, 웹브라우저에 URL http://localhost:8080/ttmay2005/hello?WSDL를 입력하여 배치된 서비스의 WSDL 파일을 보게될 것이다.. (Application Server가 port 8080에 배치되었다고 가정). 그 다음 세션(“데스크탑 클라이언트”)에 있는 wscompile 명령이 성공하기 위해 서버로부터 이 XML파일을 다운로드 하는 것에 의존하기 때문에 이를 실행할 수 있음을 주의하기 바란다.

클라이언트 클래스 생성

그 웹서비스를 배치시킨 후에는, 클라이언트 프로그램으로부터 웹서비스에 접근할 수 있다. HelloClient는 이 테크팁의 샘플 코드로 제공된 데스크탑 클라이언트 프로그램이다. 이는 MyHelloService의 sayHello 메소드를 호출한다. 원격 서비스를 위한 프록시(proxy) 를 사용하는 지역 객체인 stub을 통해 이 호출을 하게 된다. stub이 개발 시간(실행시간과 대조적)에 생성되기 때문에, 그것은 보통 정적stub(static stub)이라고 불린다. 다음은HelloClient (정적 stub을 위한 코드)에 대한 원시 코드이다.

   package staticstub;   import javax.xml.rpc.*;   public class HelloClient {       private static String endpointAddress =           "http://localhost:8080/ttmay2005/hello";       public static void main(String[] args) {           System.out.println("Endpoint address = "             + endpointAddress);           try {              Stub stub = createProxy();              stub._setProperty                (Stub.ENDPOINT_ADDRESS_PROPERTY,                 endpointAddress);               HelloIF hello = (HelloIF)stub;              System.out.println(hello.sayHello("Duke!"));           } catch (Exception ex) {              ex.printStackTrace();           }       }           private static Stub createProxy() {           return                (Stub) (new MyHelloService_Impl().                   getHelloIFPort());       }   }  
Stub 파일들 생성

이전에 언급한 대로, 클라이언트는 서비스에 접근하기 위해 stub로 불리는 로컬 프록시를 사용한다. 따라서 그 서비스에 접근하는 클라이언트를 사용할 수 있기 전에, 그 stub 파일들을 생성해야한다. 이를 실행하는 방법은 wscompile와 같은 툴을 사용하는 것이다. 좀전에 한 것처럼, wscompile툴을 호출하기 위해 ant 명령어를 구동할 수 있다. 현재 디렉토리를 그 샘플 코드를 설치한 곳 밑에 있는 ttMay2005/helloclient 디렉토리로 바꾸기 바란다. 그리고 나서, 다음의 명령을 입력한다.
   ant stubs
Stub 태스크는 다음의 독립 변수를 가진 wscompile 툴을 구동한다.
   wscompile -gen:client -d build -classpath build     config-wsdl.xml 
이 독립 변수는 wscompile이 이전에 생성된 MyHelloService.wsdl 파일을 읽도록 한다. 이 툴은 또한 WSDL 파일과 명령어 줄(command-line) 플래그에 대한 정보에 기반하는 파일들을 생성한다. 특히, “-gen” 클라이언트 플래그는 serializers와 value type과 같은 런타임 파일과 stub들을 생성하도록 wscompile에 지시한다. "-d" 플래그는 툴이 생성된 결과물을 build/staticstub 하위 디렉토리에 쓰도록 명령한다.

wscompile 툴을 구동할 때, 목표 WSDL 파일의 위치를 명시하는 구성파일도 제공해야한다. 구성파일(config-interface.xml)은 이 팁의 샘플 코드로 나와있다. 다음은 config-interface.xml 파일의 내용이다.
   <configuration      xmlns="http//java.sun.com/xml/ns/jax-rpc/ri/config">     <wsdl location="http://localhost:8080/ttmay2005/hello?WSDL"      packageName="staticstub"/>   </configuration>
packageName 속성은 그 생성된 stub에 대해 자바 패키지를 명시한다. WSDL 파일의 위치가 URL으로서 명시된다는 것을 알아두기 바란다. 이는 웹서비스에서 WSDL 파일을 요청하는 wscompile 명령을 야기한다. 이 때문에, 웹서비스는 반드시 올바르게 배치되고 성공을 위한 명령을 위해 구동되고 있어야한다. 웹서비스가 실행되지 않고 있거나 서비스가 배치된 포트가 구성 파일의 포트와 다르다면, 그 명령은 실패할 것이다.

클라이언트 클래스 컴파일하기

다음 단계는 클라이언트 클래스를 컴파일하는 것이다. 이번 테크팁의 예제를 컴파일하려면, 다음 명령을 입력한다.
   ant compile
ant 컴파일 태스크는 src/HelloClient.java를 컴파일하고, 그 구축 하위 디렉토리에 클래스 파일을 작성한다. 다음의 명령을 구동하는 것과 동일하다.
   javac -sourcepath src -d build staticstub.HelloClient.java
클라이언트 패키지화

클라이언트를 생성하는 과거 단계는 이전에 생성된 파일들을 jar 파일 안으로 패키지하는 것이다. 다음 명령을 입력한다.
   ant jar
ant jar 태스크는 ttmay2005.jar이란 이름의 파일을 생성한다. HelloClient.class를 제외하고, ttmay2005jar 안의 모든 파일들은 wscompile에 의해 생성되었음을 알아두기 바란다. 또한, wscompile가 그것이 응용 서버로부터 다운로드 한 MyHelloService.wsdl 화일로부터 읽은 정보에 기반한 HelloIF.class를 생성시켰다는 것도 알아두자.

클라이언트 구동

클라이언트를 실행하기 전에 해당하는 JAR 라이브러리(Java Web Services Developer Pack 1.5 download 참고)클래스 패스를 설정하기 바란다.
   jaxrpc/lib/jaxrpc-api.jar   jaxrpc/lib/jaxrpc-spi.jar   jaxrpc/lib/jaxrpc-impl.jar   jwsdp-shared/lib/activation.jar   jwsdp-shared/lib/mail.jar   jwsdp-shared/lib/jax-qname.jar   saaj/lib/saaj-api.jar   saaj/lib/saaj-impl.jar   jaxp/lib/endorsed/dom.jar   jaxp/lib/endorsed/sax.jar   jaxp/lib/endorsed/xalan.jar   jaxp/lib/endorsed/xercesImpl.jar
다른 방법으로는, 다음과 같이 클라이언트를 실행 시킬 때 클래스패스를 추가 시킬 수도 있다.
   ant run
이 경우 커맨드 라인 창에 다음과 같은 내용이 보인다.
   [java] Endpoint address = http://localhost:8080/ttmay2005/hello   [java] Hello Duke!
추가 정보

JAX-RPC에 대한 추가 정보는, J2EE 1.4 Tutorial의 Chapter 8: Building Web Services with JAX-RPC를 참고하기 바란다.

Back to Top

JAXB로 RELAX NG 사용하기
 

Java WSDP(Java Web Services Developer Pack) 1.5의 JAXB 라이브러리는 기본적으로 RELAX NG을 포함하고 있다. RELAX NG는 XML 문서에 대한 용어를 제공하며, 이는 DTD나 XML 스키마를 대안으로 널리 알려져있다. RELAX NG 스펙은 OASIS(The Organization for the Advancement of Structured Information Standards) 내의 RELAX NG 기술 위원회에 의해 개발되어왔다. 현재 JAXB에서의 RELAX NG 지원은 실험적일 뿐이며 공식적으로 지원되지 않는다. 그러나 만약 RELAX NG를 이용하기 원한다면, 좀 더 실험적으로 JAXB와 함께 사용할 수 있다. 이번 테크팁에서는 JAXB에서 RELAX NG를 사용하는 방법에 대해 설명한다.

RELAX NG를 사용하여 속도 높이기

만약 과거에 DTD를 사용했다면, 이 문서들의 syntax가 종종 상당히 빠르게 암호화(cryptic)한다는 것을 알 것이다. RELAX NG 표준은 XML 문서 유효화를 위해 DTD를 사용하는 것에 대해 XML 기반의 대안을 제공한다. RELAX NG는 정의되지 않은 XML 스키마대신 좀 더 더 기능적인 것을 제공하도록 노력한다. 예를 들어, 다음은 DTD이다.
   <!DOCTYPE addressBook [   <!ELEMENT addressBook (card*)>   <!ELEMENT card (name, email)>   <!ELEMENT name (#PCDATA)>   <!ELEMENT email (#PCDATA)>   ]>
다음은 이와 동등한 RELAX NG이다.
   <element name="addressBook"      xmlns="http://relaxng.org/ns/structure/1.0">     <zeroOrMore>       <element name="card">         <element name="name">           <text/>         </element>         <element name="email">           <text/>         </element>       </element>     </zeroOrMore>   </element>
두 문법 중 RELAX NG syntax가 사용하기 더 쉽다고 말할 수 있다.. 이제 sysbtax를 구축해야하는 다음의 XML 문서를 보자.
   <?xml version="1.0" encoding="UTF-8"?>   <book isbn="0345391802">    <title>  The Hitchhiker's Guide to the Galaxy    </title>    <author>Douglas Adams</author>    <character>     <name>Arthur Dent</name>     <friend-of>Ford Prefect</friend-of>    </character>    <character>     <name>Ford Prefect</name>    </character>   </book>
이 XML 문서에서, 책(book)은 명백히 ISBN 숫자, 제목(title), 저자(author), 하나 이상의 문자(character)를 포함하고 있음을 알 수 있다. 그러나 각 문자는 하나 이상의 개별적인 특성을 나타낼 수도 있다. 즉, 각 문서는 그 문서와 연계된 문법을 갖는다. 이는 다음과 같다.
  A Book must have an ISBN number, a title, an author, and it   must have one or more characters.
다음은 XML 문서를 위한 어휘들을 제공하는 RELAX NG 문법에 대한 예제이다.
   <?xml version="1.0" encoding="UTF-8"?>   <grammar xmlns="http://relaxng.org/ns/structure/1.0">     <start>       <element name="book">         <attribute name="isbn">           <text/>         </attribute>       <element name="title">         <text/>       </element>       <element name="author">         <text/>       </element>       <zeroOrMore>         <element name="character">           <element name="name">             <text/>           </element>           <optional>             <element name="friend-of">               <text/>             </element>           </optional>         </element>       </zeroOrMore>     </start>   </grammar>
이는 다음 사항을 나타내고 있다.
  • 문법은 반드시 book이라는 이름의 요소로 구성된다.
  • <book> 요소는 반드시 이와 연계된 <book isbn="0345391802">같은 ISBN 속성을 갖는다.
  • book 요소에는 반드시 <title><author> 요소가 있어야한다.
  • <title><author> 요소 안과 ISBN 속성 내에는 일반 텍스트가 있어야한다.
  • There can be zero or more <character> elements.
  • 0개 이상의 <character>요소가 있을 수 있다.
  • <character>요소는 <name>요소와, 선택적으로 <friend-of>, <since>, <qualification>요소를 가진다. 이 네 요소는 모두 텍스트로만 구성된다.
만약 XML에 대해 알고 있다면 RELAX NG은 쉬울 것이다. 사실, RELAX NG를 XML에서 재형성된 DTD로 생각해도 된다.

RELAX NG 명세

RELAX NG 문법에 대해 좀 더 자세하게 알아보자.

<start> 요소는 RELAX NG 문법이 시작되는 곳을 명시한다. 이름 속성을 가지는 곳인 <define> 요소가 하나 이상 있을 수 있다. 이 정의된 요소들은 같은 이름을 명시하는 <ref> 요소로 참조될 수 있다. 따라서, 예제는 다음과 같다.
   <grammar>     <start>       <ref name="AddressBook"/>     </start>     <define name="AddressBook">       <element name="addressBook">         <zeroOrMore>           <ref name="Card"/>         </zeroOrMore>       </element>     </define>     <define name="Card">       <element name="card">         <ref name="Name"/>         <ref name="Email"/>       </element>     </define>     <define name="Name">       <element name="name">         <text/>       </element>     </define>     <define name="Email">       <element name="email">         <text/>       </element>     </define>   </grammar>
RELAX NG는 DTDs와 다른 많은 언어들과 비슷한 숫자 패턴 정량화(numerical pattern quantification)를 사용한다. 이는 다음과 같다.

<oneOrMore>. 적어도 하나 이상으로 구성된다.

<zeroOrMore>. 하나 이상으로 구성되거나 생략될 수 있다.

<optional>. 하나의 인스턴스로 구성되거나 생략될 수 있다.

In addition, if you want to specify a sequence of patterns at the same level, there are three different elements that you can use: <choice>, <group>, or <interleave>. 이에 덧붙여, 같은 레벨에서 패턴의 시퀀스를 명시하기 원한다면 사용할 수 있는 세 개의 다른 요소, <choice>, <group>, <interleave>가 있다.

<choice>.는 하나를 가리키며, 내부의 여러 개의 요소 중 하나만이 명시될 수 있다. 예를 들면,
     <choice>       <element name='femaleName'>           <text/>       </element>       <element name='maleName'>           <text/>       </element>   </choice>
<group>.은 내부의 요소들을 그룹화한다. 전체 그룹은 외부 수식어로 작용할 수 있다. 예를 들면,
      <oneOrMore>        <group>            <element name='areaCode'>                <text/>            </element>            <element name='phoneNumber'>                <text/>            </element>        </group>   </oneOrMore>
<interleave>. 그 안에 지정된 다른 시맨틱을 준수하는 한, 차일드(child) 요소를 다른 명령에도 나타날 수 있게 한다.

<list> 요소는 토큰(token)의 시퀀스가 반드시 매치되는 패턴을 포함한다. 예를 들어 공백에 의해 나누어지는 두 개의 부동소수점 숫자를 포함하는 벡터(vector)를 갖고자 한다면, 다음과 같이 리스트를 사용할 수 있다.
   <element name="vector">     <list>       <data type="float"/>       <data type="float"/>     </list>   </element>
merging inline pattern등 다른 흥미로운 RELAX NG 문법 부분은 RELAX NG tutorial에서 좀 더 자세히 알아보기 바란다.

Birthday 예제 다시 해보기

다음 예제는 RELAX NG를 이용한 XML Binding(JAXB)를 위한 Java API를 사용하여 설명하고 있다. 이 예제의 소스 코드는 이번 테크팁의 샘플 코드인 ttmay2005-rng.jar에서 제공하고 있다. 여기에서는 지난 2005년 2월 JAXB 테크팁에서 XML 스키마를 사용한 것을 RELAX NG로 변경하였다.

다음은 예제에서 사용된 XML 파일(birthdate.xml)이다.
<?xml version="1.0"?><birthdate>    <birthdayMonth>January</birthdayMonth>    <birthdayDay>21</birthdayDay>    <birthdayYear>1983</birthdayYear></birthdate>
다음은 XML 파일을 위한 규칙을 명시하는 RELAX NG 문법이다 . 문법은 birthdate.rng 파일에 있다. 이 파일은 우선 원시 RELAX NG 형식으로 나타나고, JAXB 요소는 없다. 따라서 위의 XML 파일이 어떻게 유효화되는지 알 수 있다.
<?xml version="1.0"?><grammar xmlns="http://relaxng.org/ns/structure/1.0"         datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">           <start>     <element name="birthdate">        <element name="birthdayMonth">            <data type="string"/>         </element>                    <element name="birthdayDay">            <data type="integer"/>         </element>                   <element name="birthdayYear">            <data type="integer"/>          </element>     </element>  </start> </grammar>
이 버전은 JAXB 바인딩을 추가하여 RELAX NG 문법 내의 각각의 요소가 “xjc” 툴이 적절한 소스 코드를 생성하면 어떻게 자바 오브젝트에 바인딩되는지를 가리킨다.
<?xml version="1.0"?><grammar xmlns="http://relaxng.org/ns/structure/1.0"         datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"         xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"         xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"         jaxb:extensionBindingPrefixes="xjc"         jaxb:version="1.0">    <jaxb:schemaBindings>    <jaxb:package name="birthdate"/>  </jaxb:schemaBindings>    <start>     <element name="birthdate">        <jaxb:class name="Birthdate" />                       <element name="birthdayMonth">                <jaxb:property name="Month" />                <data type="string">                    <jaxb:javaType name="java.lang.String"                     parseMethod="new" printMethod="toString" />                  </data>            </element>                        <element name="birthdayDay">                <jaxb:property name="Day" />                <data type="integer">                    <jaxb:javaType name="java.lang.Integer"                     parseMethod="new" printMethod="toString" />                  </data>            </element>                       <element name="birthdayYear">                <jaxb:property name="Year" />                <data type="integer">                    <jaxb:javaType name="java.lang.Integer"                     parseMethod="new" printMethod="toString" />                  </data>            </element>     </element>  </start> </grammar>
이 버전은 먼저<jaxb:schemaBindings> 내의 <jaxb:package> 요소를 이용하여 정의된 모든 클래스가 "birthdate" 패키지에 있도록 명시한다. 덧붙여서,는 "Birthdate" 클래스에 매핑된다. <birthdayMonth> 요소는 RELAX NG string 데이타 타입을 확인하고 "Month."라는 이름으로 java.lang.String의 read/write 속성에 매핑될 것이다.같은 형태로, <birthdayDay><birthdayYear> 요소는 java.lang.Integer 오브젝트를 사용하는 비슷한 구조에 매핑될 것이다.

다음은 데이터를 JAXB 생성 인스턴스에서 XML 파일로 마샬Marchal)하거나, 또는 XML 파일에서 JAXB 생성 인스턴스로 언마샬(unmarshal)하기 위해 JAXB를 사용하는 Java 코드 (RELAXNGExample.java 파일 내)이다.
  import java.io.File;  import javax.xml.bind.JAXBContext;  public class RELAXNGExample {    public static void main(String[] args) throws Exception {        test(args[0]);           }        private static void test( String fileName ) throws       Exception {                        JAXBContext context = JAXBContext.newInstance("birthdate");                Object o = context.createUnmarshaller().            unmarshal(new File(fileName));         context.createValidator().validate(o);        context.createMarshaller().marshal(o,System.out);    }  }
예제를 구동하기 위해서는, 먼저 XJC 컴파일러를 사용하여 RelaxNG를 컴파일해야한다. XJC가 -relaxng 옵션으로 RELAX NG 사용하도록 요청할 수 있다. 다음의 XJC 명령을 구동하자.
   xjc -relaxng birthdate.rng -d gen-src
RelaxNG 파일 내의 옵션은 birthdate 패키지(코멘트 없이 Birthdate 인터페이스 포함)을 중심으로 하는 소스 코드를 생성할 수 있다.
package birthdate;public interface Birthdate {    java.lang.Integer getDay();    void setDay(java.lang.Integer value);    java.lang.Integer getYear();    void setYear(java.lang.Integer value);    java.lang.String getMonth();    void setMonth(java.lang.String value);}
다음으로, RELAXNGExample 클래스를 컴파일한다. gen-src 디렉토리에 클래스들이 방금 생성되었다. 그 후 컴파일된 클래스들을 구동한다. 컴파일과 구동을 쉽게 하기 위새서는 ant build tool을 이용한다. 구축 파일(build.xml)은 이 테크팁을 위한 샘플 코드에 제공되어 있다. 예제의 구축 파일이 있는 같은 디렉토리에 ant를 구동하여 구조를 설정할 수 있다. 사용자의 현재 디렉토리를 사용자가 샘플 코드를 설치한 릴렉싱 디렉토리로 변경한다. 그리고 다음 명령어를 입력한다.
   ant
다음 화면이 보여질 것이다.
   Buildfile: build.xml   compile:        [echo] Compiling the schema...        ...        [echo] Compiling the java source files...        ...      run:        [echo] Running the sample application...        [java] <?xml version="1.0" encoding="UTF-8"         standalone="yes"?>        [java] <birthdate><birthdayMonth>January</birthdayMonth>        <birthdayDay>21</birthdayDay><birthdayYear>1983        </birthdayYear></birthdate>         BUILD SUCCESSFULTotal time: 4 seconds
Additional Resources

For more information about JAXB, see Chapter 2: Using JAXB.

For more information about RELAX NG, see the RELAX NG tutorial.

For more information on the JAXB RI project.

For more information on the JAXB extensions for RELAX NG.

Back to Top
Posted by tornado
|
6. GC 관련 Parameter


GC관련 설정값을 보기전에 앞서서 ?X와 ?XX 옵션에 대해서 먼저 언급하자. 이 옵션들은 표준 옵션이 아니라, 벤더별 JVM에서 따로 제공하는 옵션이기 때문에, 예고 없이 변경되거나 없어질 수 있기 때문에, 사용전에 미리 JVM 벤더 홈페이지를 통해서 검증한다음에 사용해야한다.

1) 전체 Heap Size 조정 옵션

전체 Heap size는 ?ms와 ?mx로 Heap 사이즈의 영역을 조정할 수 있다. 예를 들어 ?ms512m ?mx 1024m로 설정하면 JVM은 전체 Heap size를 application의 상황에 따라서 512m~1024m byte 사이에서 사용하게 된다. 그림2의 Total heap size

메모리가 모자를때는 heap을 늘리고, 남을때는 heap을 줄이는 heap growing과 shirinking 작업을 수행하는데, 메모리 변화량이 큰 애플리케이션이 아니라면 이 min heap size와 max heap size는 동일하게 설정하는 것이 좋다. 일반적으로 1GB까지의 Heap을 설정하는데에는 문제가 없으나, 1GB가 넘는 대용량 메모리를 설정하고자 할 경우에는 별도의 JVM 옵션이 필요한 경우가 있기때문에 미리 자료를 참고할 필요가 있다.

※ IBM AIX JVM의 경우
%export LDR_CNTRL=MAXDATA=0x10000000
%java -Xms1500m -Xmx1500m MyApplication

2) Perm size 조정 옵션

Perm Size는 앞에서도 설명했듯이, Java Application 자체(Java class etc..)가 로딩되는 영역이다. J2EE application의 경우에는 application 자체의 크기가 큰 편에 속하기 때문에, Default로 설정된 Perm Size로는 application class가 loading되기에 모자른 경우가 대부분이기 때문에, WAS start초기나, 가동 초기에 Out Of Memory 에러를 유발하는 경우가 많다.

PermSize는 -XX:MaxPermSize=128m 식으로 지정할 수 있다.
일반적으로 WAS에서 PermSize는 64~256m 사이가 적절하다.

3) New 영역과 Old 영역의 조정New 영역은 ?XX:NewRatio=2 에 의해서 조정이 된다.
NewRatio Old/New Size의 값이다. 전체 Heap Size가 768일때, NewRatio=2이면 New영역이 256m, Old 영역이 512m 로 설정이 된다.
JVM 1.4.X에서는 ?XX:NewSize=128m 옵션을 이용해서 직접 New 영역의 크기를 지정하는 것이 가능하다.

4) Survivor 영역 조정 옵션
-XX:SurvivorRatio=64 (eden/survivor 의 비율) :64이면 eden 이 128m일때, survivor영역은 2m가 된다.

5) -server와 ?client 옵션
JVM에는 일반적으로 server와 client 두가지 옵션을 제공한다.
결론만 말하면 server 옵션은 WAS와 같은 Server환경에 최적화된 옵션이고, client옵션은 워드프로세서와 같은 client application에 최적화된 옵션이다. 그냥 언뜻 보기에는 단순한 옵션 하나로보일 수 있지만, 내부에서 돌아가는 hotspot compiler에 대한 최적화 방법과 메모리 구조자체가 아예 틀리다.

○ -server 옵션

server용 application에 최적화된 옵션이다. Server application은 boot up 시간 보다는 user에 대한 response time이 중요하고, 많은 사용자가 동시에 사용하기 때문에 session등의 user data를 다루는게 일반적이다. 그래서 server 옵션으로 제공되는 hotspot compiler는 java application을 최적화 해서 빠른 response time을 내는데 집중되어 있다.

또한 메모리 모델 역시, 서버의 경우에는 특정 사용자가 서버 운영시간동안 계속 서버를 사용하는게 아니기 때문에 (Login하고, 사용한 후에는 Logout되기 때문에..) 사용자에 관련된 객체들이 오래 지속되는 경우가 드물다. 그래서 상대적으로 Old영역이 작고 New 영역이 크게 배정된다. <그림 7. 참조 >

○ -client 옵션

client application은 워드프로세서 처럼 혼자 사용하는 application이다. 그래서 client application은 response time보다는 빨리 기동되는데에 최적화가 되어 있다. 또한대부분의 client application을 구성하는 object는GUI Component와 같이 application이 종료될때까지 남아있는 object의 비중이 높기 때문에 상대적으로 Old 영역의 비율이 높다.


<그림 7. ?server와 ?client 옵션에 따른 JVM Old와 New영역>


이 두옵션은 가장 간단한 옵션이지만, JVM의 최적화에 아주 큰부분을 차지하고 있는 옵션이기 때문에, 반드시 Application의 성격에 맞춰서 적용하기 바란다.
(※ 참고로, SUN JVM은 default가 client, HPJVM는 default가 server로 세팅되어 있다.)

○ GC 방식에 대한 옵션

GC 방식에 대한 옵션은 앞에서도 설명했지만, 일반적인 GC방식이외에, Concurrent GC,Parallel GC,Inceremental GC와 같이 추가적인 GC Algorithm이 존재한다. 옵션과 내용은 앞장에서 설명한 “다양한 GC알고리즘” 을 참고하기 바란다.


7.JVM GC 튜닝


그러면 이제부터 지금까지 설명한 내용을 기반으로 실제로 JVM 튜닝을 어떻게 하는지 알아보도록 하자.

STEP 1. Application의 종류와 튜닝목표값을 결정한다.

JVM 튜닝을 하기위해서 가장 중요한것은 JVM 튜닝의 목표를 설정하는것이다. 메모리를 적게 쓰는것이 목표인지, GC 횟수를 줄이는것이 목표인지, GC에 소요되는시간이 목표인지, Application의 성능(Throughput or response time) 향상인지를 먼저 정의한후에. 그 목표치에 근접하도록 JVM Parameter를 조정하는것이 필요하다.

STEP 2. Heap size와 Perm size를 설정한다.

-ms와 ?mx 옵션을 이용해서 Heap Size를 정한다. 일반적으로 server application인 경우에는 ms와 mx 사이즈를 같게 하는것이 Memory의 growing과 shrinking에 의한 불필요한 로드를 막을 수 있어서 권장할만하다.

ms와mx사이즈를 다르게 하는 경우는 Application의 시간대별 memory 사용량이 급격하게 변화가 있는 Application에 효과적이다.
PermSize는 JVM vendor에 따라 다소 차이가 있으나 일반적으로 16m정도이다. Client application의 경우에는 문제가 없을 수 있지만, J2EE Server Application의 경우 64~128m 사이로 사용이 된다.

Heap Size와 Perm Size는 아래 과정을 통해서 적정 수치를 얻어가야한다.

STEP 3. 테스트 & 로그 분석.

JVM Option에 GC 로그를 수집하기 위한 ?verbosegc 옵션을 적용한다. (HP의 경우 ?Xverbosegc 옵션을 적용한다.)

LoadRunner나 MS Strest(무료로 MS社의 홈페이지에서 다운로드 받을 수 있다.)와 같은 Strest Test툴을 통해서 Application에 Strest를 줘서. 그 log를 수집한다. 튜닝에서 있어서 가장 중요한것은 목표산정이지만, 그만큼이나 중요한것은 실제 Tuning한 Parameter가 Application에 어떤 영향을 주는지를 테스트하는 방법이 매우 중요하다. 그런 의미에서 적절한 Strest Tool의 선정과, Strest Test 시나리오는 정확한 Tuning을 위해서 매우 중요한 요인이다.

○ Perm size 조정
아래 그림8.은 HP JVM에서 ?Xverbosegc 옵션으로 수집한 GC log를 HP Jtune을 통해서 graph로 나타낸 그래프이다. 그림을 보면 Application이 startup되었을때 Perm 영역이 40m에서. 시간이 지난후에도 50m 이하로 유지되는것을 볼 수 있다. 특별하게 동적 classloading등이 수십m byte가 일어나지 않는등의 큰 변화요인이 없을때, 이 application의 적정 Perm 영역은 64m로 판단할 수 있다.


<그림 8. GC 결과중 Perm 영역 그래프>


○ GC Time 수행 시간 분석

다음은 GC에 걸린 시간을 분석해보자. 앞에 강좌 내용에서도 설명햇듯이. GC Tuning에서 중요한 부분중 하나가 GC에 소요되는 시간 특히 Full GC 시간이다.

지금부터 볼 Log는 모社의 물류 시스템의 WAS 시스템 GC Log이다. HP JVM을 사용하며, -server ?ms512m ?mx512m 옵션으로 기동되는 시스템이다.

그림 9를 보면 Peak 시간 (첫번째 동그라미) 14시간동안에 Full GC(동그란점)가 7번일어난것을 볼 수 있다. 각각에 걸린 시간은2.5~6sec 사이이다.
여기서 STEP 1.에서 설정한 AP Tuning의 목표치를 참고해야하는데.

Full GC가 길게 일어나서 Full GC에 수행되는 시간을 줄이고자 한다면 Old 영역을 줄이면 Full GC가 일어나는 횟수는 늘어나고, 반대로 Full GC가 일어나는 시간을 줄어들것이다.

반대로 Full GC가 일어나는 횟수가 많다면, Old 영역을 늘려주면 Full GC가 일어나는 횟수는 상대적으로 줄어들것이고 반대로 Full GC 수행시간이 늘어날 것이다.

특히 Server Application의 경우Full GC가 일어날때는 JVM자체가 멈춰버리기 때문에, 그림 9의 instance는 14시간동안 총 7번 시스템이 멈추고, 그때마다 2.5~6sec가량 시스템이 response를 못하는 상태가 된것이다. 그래서 멈춘 시간이 고객이 납득할만한 시간인지를 판단해야 하고, 거기에 적절한 Tuning을 해야한다.

Server Application에서 Full GC를 적게일어나게하고, Full GC 시간을 양쪽다 줄이기 위해서는 Old영역을 적게한후에, 여러개의 Instance를 동시에 뛰어서 Load Balancing을 해주면, Load가 분산되기 때문에 Full GC가 일어나는 횟수가 줄어들테고, Old 영역을 줄였기 때문에, Full GC에 드는 시간도 줄어들것이다. 또한 각각의 FullGC가 일어나는동안 하나의 서버 instance가 멈춰져 있어도, Load Balancing이 되는 다른 서버가 response를 하고 있기때문에, Full GC로 인한 Application이 멈추는것에 의한 영향을 최소화할 수 있다.


<그림 9. GC 소요시간>


데이타에 따라서 GC Tuning을 진행한후에는 다시 Strest Test를 진행해서 응답시간과 TPS(Throughput Per Second)를 체크해서 어떤 변화를 주었는지를 반드시 체크해봐야한다.


<그림 10. GC후의 Old 영역>


그림 10은 GC후에 Old 영역의 메모리 변화량을 나타낸다.

금요일 업무시간에 메모리 사용량이 올라가다가. 주말에가서 완만한 곡선을 그리는것을 볼 수 있다. 월요일 근무시간에 메모리 사용량이 매우 많고, 화요일에도 어느정도 메모리 사용량이 있는것을 볼 수 있다. 월요일에 메모리 사용량이 많은것을 볼때, 이 시스템의 사용자들이 월요일에 시스템 사용량이 많을 수 있다고 생각할 수 있고, 또는 다른 주의 로그를 분석해봤을때 이 주만 월요일 사용량이 많았다면, 특별한 요인이나 Application 변경등이 있었는지를 고려해봐야할것이다.

이 그래프만을 봤을때 Full GC가 일어난후에도 월요일 근무시간을 보면 Old 영역이 180M를 유지하고 있는것을 볼 수 있다. 이 시스템의 Full GC후의 Old영역은 80M~180M를 유지하는것을 볼 수 있다. 그래서 이 시스템은 최소 180M이상의 Old 영역을 필요로하는것으로 판단할 수 있다.

STEP 4. Parameter 변경.
STEP 3에서 구한 각 영역의 허용 범위를 기준으로 Old영역과 New 영역을 적절하게 조절한다.
PermSize와 New영역의 배분 (Eden,Survivor)영역등을 조정한다.
PermSize는 대부분 Log에서 명확하게 나타나기 때문에, 크게 조정이 필요가 없고 New영역내의 Eden과 Survivor는 거의 조정하지 않는다. 가장 중요한것은 Old영역과 New 영역의 비율을 어떻게 조정하는가가 관건이다.

이 비율을 결정하면서, STEP1에서 세운 튜닝 목표에 따라서 JVM의 GC Algorithm을 적용한다. GC Algorithm을 결정하는 기본적인 판단 내용은 아래와 같다.



이렇게 Parameter를 변경하면서 테스트를 진행하고, 다시 변경하고 테스트를 진행하는 과정을 거쳐서 최적의 Parameter와 GC Algorithm을 찾아내는것이 JVM의 메모리 튜닝의 이상적인 절차이다.


지금까지 JVM의 메모리 구조와 GC 모델 그리고 GC 튜닝에 대해서 알아보았다.

정리하자면 GC 튜닝은 Application의 구조나 성격 그리고, 사용자의 이용 Pattern에 따라서 크게 좌우 되기때문에, 얼마만큼의 Parameter를 많이 아느냐 보다는 얼마만큼의 테스트와 로그를 통해서 목표 값에 접근하느냐가 가장 중요하다
Posted by tornado
|

 http://phpschool.com/bbs2/inc_view.html?id=12307&code=tnt2&start=0&mode=&field=&search_name=&operator=&period=&category_id=&s_que=

 

 

UNICODE :

http://www.unicode.org/standard/translations/korean.html

유니코드에 대해 ?

어떤 플랫폼,
어떤 프로그램,
어떤 언어에도 상관없이
유니코드는 모든 문자에 대해 고유 번호를 제공합니다.



UCS-2 :

대부분의 흔히 쓰는 문자들을 정의한 규격입니다.
2bytes 범위라서 UCS-2 입니다.
이걸 bit 로 표현하여 UTF-16 입니다.
UTF-16LE, UTF-16BE 가 동일한 규격으로 Little Endian, Big Endian 은 단지 byte order (바이트 순서)가 다를뿐 입니다.
iconv --list 를 해보면 쓸데없이 많이 나오는데,
UTF-16LE, UCS-2LE 가 같은거고 BE 끼리 같은거라고 보면 됩니다.
그냥 UTF-16은 UTF-16LE 와 동일하나 앞에 BOM 헤더가 붙습니다.
UCS-2 는 헤더가 붙지 않습니다.


UCS-4 :

UCS-2 의 확장입니다.
뒤의 2bytes 는 UCS-2 와 완전히 호환됩니다.
즉, UCS-2 의 0xFFFF 는 UCS-4 의 0x0000FFFF 와 같은 코드입니다.
UTF-32 로 말만 바꾸어 위의 내용과 동일합니다.
인터넷 브라우저 내부에서 이것이 사용되며,
js 등에서 indexOf() 로 가져오면 UCS-4 코드가 10진수로 반환됩니다.
10진수 이므로 65535 까지는 UCS-2 와 완전 호환 됩니다.


UTF-8 :

UCS-2, UCS-4 는 영어권에서는 분명한 낭비가 있습니다.
ascii 만으로도 충분히 표현 가능한데, 한글자에 쓰지도 않는 바이트가 낭비되지요.
그런점도 있고, 문자열로 뭘 하기에 UTF-7 보다는 편리해서 가장 보편적으로 사용됩니다.
가변길이를 가지는 특징이 있습니다.
단지 계산만으로 UCS-2, UCS-4 규격으로의 상호변환이 가능합니다.
한국어는 UCS-2 규격 내에 있기 때문에 3bytes 내에서 표현 가능합니다.


UTF-7 :

이메일 등 ascii 만으로 표현해야 할 필요성에 의해 만들어졌습니다.
한 글자당 8bit 씩 할당하지만 사용은 7bit 만 합니다.
UTF-8 과 같은 특징을 가지고 있으나,
모든 ascii 값이 실제 ascii 와 같은 의미가 아니기 때문에 이것으로 무엇을 하기는 좀 힘듭니다.



BOM (Byte Order Mark) :

유니코드 종류가 많기 때문에 앞에 이런 헤더를 붙여서 구분하기도 합니다.
EmEditor, UltraEdit, Vim 등의 에디터에서 인식합니다.



코드표

http://www.unicode.org/charts/

각 나라별 코드범위와 정의된 문자를 볼 수 있습니다.
앞에 0 을 적지 않았기 때문에 (Zerofill 이 아니기 때문에) 4자리까지는 UCS-2 려니 하시고,
5 자리 부터는 UCS-4 려니 하십시오.


resy우리말로 된 유니코드 튜토리얼이 있었으면 좋겠다 했는데..
이런 자료라도 참 절실하군요. 아직도 유니코드 = UTF-16(or 다른 인코딩) 으로 착각하는 사람이 많은 현실에...

누군가 총대를 매긴 해야하는데... 거참... ^^:
07/13 2:23:12 코멘트 지우기
resy보충 내용으로...
UCS 는 코드값의 테이블이라고 생각하면 됩니다. UTF 는 인코딩의 방법(즉, 바이트의 연속된 순서를 어떻게 표현할 것이냐 하는 정의)이고, UCS 는 미리 정의되어 있는 각 글자 코드를 테이블 화 해놓은 것입니다. 가령 글자 '가' 는 유니코드에서 U+AC00 에 해당하는데, UCS2 에서는 0xAC00 테이블 좌표에 위치하고 있습니다. 이것을 UTF-8 인코딩하면, 0xEAB080 이 됩니다.

누구도 이런 식으로 설명해주는 사람이 없어서 예전엔 UCS2 = UTF16?? 이라고 헷갈리고 헤맸는데, 이게 정확한 설명인지 모르겠습니다. 잘못된 점이 있으면 누가 지적해주시길... ^^;

문자 집합(Character Set)이랑 인코딩(Encoding)에 대한 차이도 뭐 속시원히 가르쳐주는 데가 없더군요. 결국 시간이 지나다보니 스스로 알게 되었습니다만.. 확실히 외국 자료 빼면 국내는 -_-;

그러고보니 예전에 누군가가 국가 코드표와 인코딩이 가지는 의미에 대해 글 올렸던 거 같은데, locale 에 대한 내용이 그 후로 안올라오는 거 같기도...
07/13 5:19

 

 

Posted by tornado
|

IBM 에서 본 글인데... 블로그에 담으면 옆으로 퍼져서 안담았었는데..

DBGuide 에서 깨끗이 정리해놨네 ㅎㅎ

 

AOP와 메타데이터: 완벽한 조합, Part 1

메타데이터로 강화된 AOP의 개념과 구조

Ramnivas Laddad│IBM

J2SE 5.0의 일부인, 새로운 자바 메타데이터 장치는 지금까지 자바에 추가된 것 중 가장 중요한 것이다. 프로그램 엘리먼트에 추가 데이터를 첨부하는 표준 방식을 제공함으로서 메타데이터 장치는 설정 관리, 프레임웍 구현, 코드 생성 등 애플리케이션 개발의 많은 영역들을 단순화하고 향상시킬 수 있게 된다. 이 장치는 Aspect 지향 프로그래밍(AOP)에 중요한 영향을 끼친다.

메타데이터와 AOP의 결합은 중요한 질문들을 만들어낸다:

  • 메타데이터 장치가 AOP에 미치는 영향은 무엇인가?
  • 메타데이터로 강화된 AOP는 선택인가? 아니면 필수인가?
  • 메타데이터와 AOP를 효과적으로 사용하는 가이드라인은 무엇인가?
  • 이 두 결합이 AOP의 채택에 어떻게 영향을 주는가?

위 질문에 대한 대답을 AOP@Work 시리즈를 통해 설명하겠다. Part 1에서는 메타데이터의 개념과 새로운 자바 메타데이터 장치를 설명하겠다. 또한, 메타데이터를 제공하는 것과 이를 소비하는 것 사이의 차이를 설명하고 메타데이터 주석을 도입한 프로그래밍 시나리오도 소개하겠다. 그런 다음, AOP의 join point의 기초를 살펴보고 메타데이터 강조를 통해 얻을 수 있는 유익이 무엇인지를 설명하겠다. 마지막으로 실용 예제를 설명하면서 Part 1을 끝맺도록 하겠다. Part 2에서는, 다차원 비지니스 공간에서 하나의 신호로서 메타데이터를 보는 새로운 방식을 설명하겠다. 또한 AOP 채택에 메타데이터가 어떤 영향을 끼치는 지와 AOP와 메타데이터를 효과적으로 결합하는 가이드라인을 설명할 것이다.

이 글 전반에 걸쳐 제시된 모든 개념들은 세 가지 유명한 AOP 구현을 통해 현실화 될 것이다: AspectJ, AspectWerkz, JBoss AOP

메타데이터 개념

메타데이터는 데이터에 대한 데이터이다. 프로그래밍 언어라는 정황 속에서, 메타데이터는 메소드, 필드, 클래스, 패키지 같은 프로그램 요소들에 첨부된 추가 정보이다. 메타데이터는 주석(annotation)이라고 하는 프로그램 요소를 사용하여 표현된다. 메타데이터와 관련된 문법은 단순한 문서에서부터 실행-작동의 변경 까지 적용된다. 예를 들어, 클래스의 작성자와 저작권 소유자를 설명하는데 메타데이터를 사용할 수 있다. 프로그램 실행에 아무 영향이 없는 경우에 사용할 수 있고 또는 메소드의 작동을 변경할지도 모르는 트랜잭션 특성 같은 메소드 속성을 설명할 때도 이를 사용할 수 있다.

자바에는 다양한 메타데이터 툴들이 있는데(XDoclet은 가장 잘 알려져 있다), 메타데이터 주석은 Java 5.0과 함께 핵심 자바로 통합되었다. 자바 메타데이터 장치(JSR 175; 참고자료)에는 커스텀 주석을 자바 코드에 추가하는 장치가 포함되어 있고 리플렉션을 통해 메타데이터 주석으로 프로그래밍 방식의 접근을 제공한다.

메타데이터를 이해하고 사용하는 핵심은 공급과 소비의 개념이다. 메타데이터 공급자는 주석 인스턴스들과 프로그램 요소들을 제휴시키는 장치인 반면, 소비자는 주석 인스턴스를 읽고, 해석하고, 이에 대해 실행하는 것이다. 다음 섹션에서 이 개념들을 자세히 설명하겠다.

메타데이터 공급하기

메타데이터 장치는 주석을 프로그램 엘리먼트에 공급하는 장치를 정의한다. 선택적으로 메타데이터 장치는 주석 유형을 정의하는 방식을 지정할 수 있다. 주석 유형은 템플릿을 지정하여 주석 인스턴스를 만든다. 클래스가 템플릿을 지정하여 객체들을 생성하는 것과 많이 비슷하다. 메타데이터 장치는 주석을 소비하는 일반적인 방식을 지정하기도 한다. 다만 한가지, 주석과 연관된 해석과 문법을 정의하지 않는다. 이 일은 소비자의 몫이다.

메타데이터 장치들의 기능은 다양하다. 예를 들어, Javadoc 스팩은 프로그램 요소와 연관된 코멘트에 주석을 정의한다. 어떤 메타데이터 장치들은 개별 문서(주로 XML 문서)를 사용하여 메타데이터를 표현한다. 예를 들어, EJB 전개 디스크립터는 엔터프라이즈 빈의 추가 특성을 지정한다. Javadoc 장치와는 달리 이 접근방식은 프로그램 요소들과 메타테이터를 약결합한다. 단점은 개발자들이 같은 엘리먼트를 설명할 때 많은 소스들을 변경해야 한다.

자바 메타데이터 장치는 메타데이터에 새로운 언어 지원을 추가하여 주석 유형을 선언하고 프로그램 요소에 주석을 단다. 또한 클래스 파일의 소스 코드 레벨에 메타데이터를 보유하는 것도 가능하다. 런타임 시 보유 정책으로 제어된다.

메타데이터 공급자는 다중 엘리먼트에 주석을 개별적으로 공급하기 보다 크로스커팅 방식으로 특정 주석을 첨부하는 장치를 포함하고 있다. 이러한 특징 때문에 AOP는 주석을 잘 공급할 수 있다.

메타데이터 장치의 선택은 메타데이터를 표현하는 방식에 영향을 미치지만 추가 데이터들을 프로그램 엘리먼트에 연결시키는 근본적인 개념은 모든 메타데이터 장치들에 모두 일반적이다.

메타데이터 소비하기

메타데이터 주석에서 값을 만들기 위해서는 이를 소비하는 것이 필요하다. 메타데이터는 다양한 방식으로 소비될 수 있고 이러한 사용법을 이해하면 AOP와 메타데이터를 올바르게 결합할 수 있다. 다음의 사용 케이스들은 비 AOP 목적으로 제공된 주석이 AOP 구현에서 소비되는 방법을 이해하는데 도움이 된다.

코드 생성
코드 생성은 메타데이터를 사용하는 가장 익숙한 방식이다. XDoclet 같은 툴을 사용하여 Javadoc 태그로 지정된 주석을 소비하여 XML 문서 또는 자바 코드 같은 객체들을 만들 수 있다. 생성된 코드는 주석이 달린 엘리먼트의 런타임 작동에 영향을 준다. 자바 메타데이터 장치를 지원하는 새로운 XDoclet가 개발되고 있다. 명령행 유틸리티인 apt (Annotation Processing Tool)- Java 2 SDK 5.0의 일부-는 플러그인을 작성하여 주석을 처리하는 방식을 제공한다. 예를 들어, 최근에 발표된 Contract 실행 툴인 Contract4Jsms apt를 사용하여 design-by-contract (DBC) 콘트랙트를 실행하는 Aspect를 만든다.

프로그래밍 방식의 작동 변경
표준 메타데이터 장치는 런타임 시 사용할 수 있는 방식을 제공한다. 또한 프로그래밍 방식으로 리플렉션을 사용하여 주석 인스턴스에 접근할 수 있도록 한다. 주석 인스턴스는 다른 객체들 처럼 사용되어 프로그램의 작동을 변경한다. 이 같은 프로그래밍 방식의 소비는, 생성된 객체들이 이 주석에서 인코딩된 정보를 읽힐 수 있는 곳에서, 프로그래머들이 애플리케이션의 코드 생성 루트를 지나치도록 한다.

프레임웍의 소비
메타데이터는 프로그램 엘리먼트와 프레임웍 간 통신, 또는 EJB, EMF, TestNG 같은 툴을 활용하는데 일반적으로 사용된다. 프레임웍 자체는 코드 생성, 리플렉션을 사용한 접근, 또는 AOP를 사용하여 특정 로직을 실행에 적용한다. @Remove@Session같은 EJB 3.0에서의 주석의 사용은 프로그램 엘리먼트의 역할인 프레임웍과 통신한다. Eclipse Modeling Framework은 주석을 사용하여(현재 Javadoc 태그로 표현됨) UML 모델을 만들고 XML 영속성을 지원한다. TestNG는 메타데이터를 테스트 케이스와 테스트 실행 툴들 간 통신에 사용한다.

언어 확장
메타데이터의 사용은 기저의 프로그래밍 언어와 컴파일러로 확대된다. 문법 속성들을 메타데이터와 제휴 시킨다는 것은 제휴하지 않은 것과는 다른 구조와 작동을 갖게 된다는 것을 의미한다. (참고자료) 최근에 발표된 Pluggable Annotation Processing API (JSR 269 )는 표준 방식으로 주석을 처리한다. 자바를 확장하는 메타데이터의 사용은 강력하면서도 위험하다: 주석은 핵심 언어를 변경하지 않고 자바 언어에 새로운 기능을 추가할 수 있도록 해주어 이를 개방 언어로 만든다. 최고의 시나리오에서 원리를 갖춘 확장은 호스트 언어의 몇 가지 한계를 극복 할 수 있다. 한편 비표준 또는 통일성 없는 주석들은 이해가 안되는 코드를 만들 뿐이다.

평범한 객체 지향 언어에서 AOP를 실행시키는 것은 언어 확장에 메타데이터를 사용하는 한 가지 예이다. AspectWerkz와 JBoss AOP는 메타데이터를 사용하여 Aspect로서 클래스의 문법을, pointcut으로서 데이터 필드를, 어드바이스로서 메소드를 변경한다. AspectJ 5는 @AspectJ 문법을 지원하는데 이는 AspectJ와 AspectWerkz 프로젝트의 합병 결과이다. 다른 AOP 구현과 자바 언어 확장에 대한 자세한 정보는 참고자료를 참조하라.

다음 섹션에서는 AOP의 join point 모델의 기초를 설명하고 메타데이터로 강화하는 것의 효용을 설명하겠다.

메타데이터와 join point 모델

A join point는 시스템 실행 시 구분 포인트이다. AOP의 가장 근본적이면서 독창적인 개념인 join point model모델은 시스템에서 어떤 join point가 노출되고 이들이 어떻게 잡히는지를 정의한다. Aspect를 사용하여 크로스커팅 기능을 구현하려면 pointcut이라고 하는 프로그래밍 구조를 사용하여 필요한 join point를 잡아야 한다.

Pointcuts은 join point를 선택하고 선택된 join point에서 콘텍스트를 모은다. 모든 AOP 시스템은 pointcut을 정의하는 언어를 제공한다. pointcut 언어의 난해함은 다양한 AOP 시스템들 간 요소를 차별화한다. pointcut 언어가 성숙할수록 강력한 pointcut을 작성하기는 더 쉽다.

join point 잡기

pointcut은 프로그램의 기존 엘리먼트의 속성을 지정한다. 이 작업의 주요 부분과 좋은 Aspect를 작성하는 원리는 강력한 pointcut을 작성하는데 있다; 다른 요소로는 설계가 잘 된 Aspect이다. 기대했던 것보다 더 많은 join point를 잡거나 기대했던 join point를 놓치는 pointcut은 시스템 진화의 걸림돌이다. 좋은 pointcut을 작성하는 것이 AOP를 파악하는 핵심이다.

현재 join point를 잡는 가장 일방적인 방식은 프로그램 엘리먼트의 임의의 속성들을 활용하는 것이다. 서명(signature) 같은 정적 속성들(유형과 메소드 이름, 인자 유형, 리턴 유형과 예외 유형 등)과 어휘 배치와 제어 흐름 같은 동적 속성들이 이에 포함된다. join point 서명의 와일드 카드들을 공정하게 사용하면 훌륭한 pointcut 정의가 된다. 개별 pointcut 들을 합성하여 보다 복잡한 것을 만들 수 있다. 프로그램 엘리먼트의 임의 속성에 기반한 join point 모델은 강력하고 유용하다. 여러 제품에서 AOP의 성공이 이를 증명하고 있다.

프로그램 엘리먼트의 서명에 사용할 수 있는 임의의 정보는 필요한 join point를 잡기에 충분하다. 이 모델에서 동적 크로스커팅이라고 하는 임의 데이터의 결합인 와일드카드와 제어 흐름 같은 동적 속성들은 잡힌 프로그램 엘리먼트를 변경하지 않고 join point를 잡을 수 있다. 예를 들어 Remote 인터페이스를 구현하는 클래스에서 RemoteException을 던지는 연산을 지정하여 모든 RMI 호출을 잡을 수 있다. (AspectJ에서 정의된) execution(* Remote+.*(..) throws RemoteException) 같은 pointcut은 프로그램 엘리먼트를 변경하지 않고 모든 RMI 연산을 깔끔하게 잡고 강력한 pointcut도 보장한다. 가장 큰 장점은 RMI 인프라가 필요로 하는 것 외에 추가 협업 없이 join point를 잡을 수 있다는 점이다.

메타데이터로 join point 잡기

서명 기반의 pointcut은 특정 크로스커팅 객체를 구현하는데 필요한 join point를 잡을 수 없다. 예를 들어, 트랜잭션 관리 또는 권한을 필요로 하는 join point를 어떻게 잡겠는가? RMI 예와는 달리, 엘리먼트의 이름 또는 서명에 내재된 어떤 것도 트랜잭션 또는 권한 특성을 암시하지 않는다. 이 상황에 필요한 pointcut은 다음 예제에서 직접 보겠지만 다루기 힘들다. (이 예제는 AspectJ에 있지만 다른 시스템의 pointcut은 개념상 동일하다.)

pointcut transactedOps()     : execution(public void Account.credit(..))      || execution(public void Account.debit(..))      || execution(public void Customer.setAddress(..))        || execution(public void Customer.addAccount(..))      || execution(public void Customer.removeAccount(..));

이 같은 상황은 메타데이터 사용을 도입하여 필요한 join point를 잡는다. 예를 들어, 아래에서 보는 것 처럼 pointcut을 작성하여 @Transactional 주석을 실행하는 모든 메소드의 실행을 잡을 수 있었다.

pointcut execution(@Transactional * *.*(..));

메타데이터와 모듈성

위 예제에서 메타데이터를 사용하여 join point를 잡는 것이 어리석은 것 처럼 보이지만 그 같은 사용 방식의 함축성을 생각하는 것은 중요하다. 특히 모듈성에 적용될 때는 더욱 그렇다. 일단 pointcut에 메타데이터를 사용하기 시작하면 메소드는 해당 주석을 실행하여 이들을 사용하는 Aspect의 크로스커팅 구현에서 협업해야 한다:

public class Account {    ...    @Transactional(kind=Required)    public void credit(float amount)  {        ...    }    @Transactional(kind=Required)    public void debit(float amount)  {        ...    }	    public float getBalance() {        ...    }    ...}

이와 비슷하게, Customer클래스의 addAccount(), removeItem(), setAddress() 메소드는 이제 @Transactional 주석을 실행해야 한다.

대부분의 AOP 사용자들은 Aspect 특성을 활용하는 디자인 패턴을 통해 현재 기존 AOP 지원에 트랜잭션과 권한부여 부분을 구현하고 있다. 이 글에서 보듯, AOP 시스템에 메타데이터를 추가하면 시스템을 놀랍게 향상시킬 수 있다. 추가된 메타데이터가 AOP 시스템의 모듈성에 어떻게 영향을 미치는 지와 메타데이터가 가장 유용하게 사용될 시나리오는 Part 2에서 다루겠다. 다음 섹션에서는 AOP 구현이 결합 메타데이터로 확장되는 방법을 구체적으로 설명하겠다.

메타데이터로 강화된 AOP

AOP 시스템과 이들의 join point 모델은 메타데이터 주석을 소비함으로서 증가할 수 있다. JBoss AOP, Spring AOP, AspectWerkz, AspectJ는 메타데이터를 활용하는 메커니즘을 제공하고 있거나 제공할 계획이다. JBoss AOP와 AspectWerkz는 현재 버전에 메타데이터를 지원한다. Spring AOP는 pointcut을 프로그램방식으로 작성할 수 있도록 하고 org.springframework.aop.Pointcut 인터페이스를 구현하는 것으로 메타데이터를 지원한다. AspectJ의 차기 버전은 AspectJ 언어를 변경함으로서 메타데이터를 지원할 것이다.

이전 섹션에서 @Transactional 주석으로 메소드를 집어내는 예제를 사용하여 AOP 시스템이 메타데이터를 소비하는 방법의 기초를 설명했다. 이제 부터는 AOP와 메타데이터를 결합하는 부분에 초점을 맞춰 설명하겠다.

이 글의 예제가 메타데이터를 지원하는 AOP 구현에 초점을 맞추고는 있지만 핵심 AOP 시스템이 직접 이를 지원하지 않을 때도 메타데이터를 소비하는 것이 가능하다. 코드 생성 지원에 피기백을 활용한다. 예를 들어, Barter는 오픈 소스 툴로서, 주석과 코드 생성 사전 단계를 사용해서, Javadoc 태그에 기반한 join point 캡쳐를 지원하지 않는 AspectJ의 구 버전과의 DBC 콘트랙트를 실행한다. 요즘, Contract4J는 자바 메타데이터 장치 스타일의 주석을 사용하여 이와 비슷한 일을 수행한다.

AOP 시스템의 메타데이터 지원

메타데이터 기반의 크로스커팅을 지원하려면 AOP 시스템은 주석을 소비하고 공급하는 방식을 제공해야 한다. 여기에서 두 가지 지원 유형의 기초를 설명하겠다. 다음 섹션에서 보다 자세히 설명하겠다.

주석 소비 지원
주석 소비를 지원하는 AOP 시스템에서는 프로그램 엘리먼트와 제휴된 주석에 기반하여 join point를 선택할 수 있다. 이 같은 지원이 되는 현재 AOP 시스템에서는 다양한 서명 패턴의 정의를 확장하여 주석 유형과 속성들이 지정되도록 한다. 예를 들어, pointcut은 Timing유형의 주석을 실행하는 모든 메소드를 선택한다. 게다가 25를 초과하는 속성값을 지닌 메소드만 하위로 선택할 수 있다. 주석 유형과 속성에 의존한 어드바이스를 구현하려면 시스템은 join point와 제휴된 주석 인스턴스를 잡는 pointcut 신택스를 추가할 수 있다. 마지막으로, 시스템은 어드바이스가 반사적인 API를 통해 주석 인스턴스에 접근할 수 있도록 한다.

주석 공급 지원
표준 자바 메타데이터 장치를 사용하여 하나의 주석 인스턴스는 주석이 달리고 있는 각 프로그램 엘리먼트를 위해 선언된다. 하지만 다중의 프로그램 엘리먼트들에 같은 주석 선언이 나타난다면 불필요한 것이 되고 만다. AOP 크로스커팅 메커니즘을 사용하여 모든 효력이 적용되는 엘리먼트에 한번만 주석을 선언하도록 할 수 있다. 주석 공급을 지원하는 AOP 시스템에서는 크로스커팅 방식으로 프로그램 엘리먼트에 주석을 붙일 수 있다. 예를 들어, @Secure 주석을 간단한 선언과 함께 Account 클래스의 모든 메소드에 붙일 수 있다. 모든 메소드에 걸쳐 주석을 낭비할 필요가 없다.

모든 AOP 시스템이 앞서 언급한 모든 기능들을 지원하는 것은 아니다. 이어지는 섹션을 통해 보다 자세히 배우게 될 것이다. 여러 AOP 시스템들이 어떻게 주석 소비를 지원하는지 볼 것이다.

AOP에서 주석 소비하기

pointcut 신택스는 다양한 메타데이터가 강화된 AOP 시스템과는 다르다. @Transactional 주석 인스턴스를 수반하는 모든 메소드를 잡는 pointcut을 각 시스템이 어떻게 핸들하는지를 연구하면 그 차이를 알 수 있다. 이 예제에서는, 주석 유형 별로 join point를 선택하는 것에 초점을 맞추겠다; 그런 다음 join point 선택에 영향을 미치는 다른 요소들을 설명하겠다.

AspectJ5
AspectJ 5 신택스는 유형, 메소드, 필드에 대한 서명의 정의를 확장하여 메타데이터 주석을 서명의 일부로 포함시킨다. 다음과 같다:

pointcut transactedOps(): execution(@Transactional * *.*(..)); 

AspectJ 5에서 @AspectJ 스타일의 정의를 사용한다면 같은 pointcut이 다음과 같이 정의된다:

@Pointcut("execution(@Transactional * *.*(..))")void transactedOps();

AspectWerkz
대부분의 다른 AOP 툴과 마찬가지로, AspectWerkz의 pointcut 신택스는 AspectJ의 그것과 닮았다. XML 스타일이 동일한 pointcut 식을 갖고 있지만 다음 코드에서 pointcut 선언은 메타데이터 유형의 주석을 사용한다:

@Expression("execution(@Transactional * *.*(..))")Pointcut transactedOps;

AspectWerkz는 메타데이터를 사용하여 AOP를 지원하도록 자바를 확장한다. 따라서 AspectWerkz는 프로그래밍 엘리먼트의 문법을 확장하기 위해서 그리고 메타데이터에 기반한 크로스커팅을 구현하기 위해서 메타데이터를 사용한다. 위 예제에서 후자의 용법에 초점을 맞추었다.

JBoss AOP
JBoss AOP는 다른 신택스를 사용하지만 기타 AOP 시스템들과 개념적으로는 많이 다르지 않다. 이곳에 나타난 pointcut은 다른 예제와 동일하지만 Jboss의 XML 신택스로 표현된다:

<pointcut name="transactedOps" expr="* *->@Transactional(..)"/>

다른 AOP 시스템들이 첨부된 메타데이터 주석에 근거하여 join point를 잡는 방법은 개념적으로 차이가 별로 없다.

주석 속성별로 선택하기

join point를 선택할 때 유형만 고려해서는 안된다. 속성 역시 고려되어야 한다. 예를 들어, 다음의 pointcut은 RequiredNew로 설정된 속성 값을 가진 @Transactional 주석을 가진 모든 메소드들을 잡는다:

execution(@Transactional(value==RequiredNew) *.*(..)) 

작성할 때, 어떤 AOP 시스템도 주석 속성에 기반한 pointcut을 지원하지 않고 대신 각 시스템은 어드바이스 내부의 동적 결정에 근거하여 속성들을 검사하고 해당 로직을 호출한다. (또는 적어도 if() pointcut을 사용하여 동적 체크를 사용한다.) 주석 속성 기반의 pointcut은 컴파일 시간 체크와 정적 선택에 특히 유용하다. AOP 시스템의 차기 버전들은 이러한 종류의 pointcut을 지원할 것이다.

주석 인스턴스 노출하기

어드바이스 로직이 메타데이터 주석 유형인스턴스 속성에 의존하기 때문에 각 주석 인스턴스는 join point의 다른 콘텍스트와 같은 방식으로 콘텍스트로서 노출되어야 한다. (예를 들어, 객체, 메소드 인자 등). AspectJ 5는 기존 pointcut을 확장하고 새로운 것을 추가하여 주석을 노출한다. 예를 들어, 다음의 pointcut은 캡쳐된 join point와 제휴된 Transactional유형의 주석 인스턴스를 모은다:

pointcut transactedOps(Transactional tx)    : execution(@Transactional * *.*(..)) && @annotation(tx);

일단 캡쳐되면, 주석 인스턴스는 다른 콘텍스트와 같은 방식으로 사용된다. 예를 들어, 다음 어드바이스에서 캡쳐된 주석은 이것의 속성을 묻고있다:

Object around(Transactional tx) : transactedOps(tx) {    if(tx.value() == Required) {        ... implement the required transaction behavior    } else if(tx.value() == RequiredNew) {        ... implement the required-new transaction behavior    }    ...}

대부분의 AOP 시스템은 반사적인 API만을 사용하여 캡쳐 된 join point에서 주석 인스턴스를 노출하고 주석들을 join point 콘텍스트로서 바인딩하지 못하도록 한다. 이 경우, 이러한 제휴 주석들에 대한 권장 join point를 나타내는 객체를 쿼리할 수 있다. AspectJ는 반사 액세스와 전통적인 join point 콘텍스트를 제공한다.

AOP에서 주석 공급하기

AOP 구조를 사용하여 주석을 공급하는 기본 개념은 프로그램 엘리먼트의 정의를 주석으로 어지럽히지 않는다는 것이다. 개념상, 그와 같은 구조에서는 크로스커팅 방식으로 프로그램 엘리먼트에 주석을 붙일 수 있다. 언뜻 보기에, AOP 구조를 사용하여 주석을 공급하고 그런 다음 그 주석을 사용하여 join point를 잡는 것은 불필요한 것 처럼 보인다. 결국 주석을 필요로 하는 join point를 구별할 수 있다면 pointcut을 작성하고 이들에게 join point를 직접 권고할 수 있다. 하지만 크로스커팅 방식으로 주석을 공급하는 것은 매우 도움이 된다. 우선, 그 같은 선언은 비 AOP 클라이언트들과 통신할 때 연결관으로서 작동한다. 더욱이, 크로스커팅 메커니즘으로 주석을 공급하면 주석 클러터(clutter)를 피하면서 약결합 시스템을 설계할 수 있다.

AOP 구조를 사용하는 다른 디자인에 대해서는 이 글 말미에 설명하도록 하겠다. 이제는 크로스커팅 방식으로 주석을 공급하는 기본 신택스를 설명하겠다.

주석 신택스 공급하기

AspectJ의 제안 신택스는 현재 정적 크로스커팅 구조를 확장하여 새로운 declare annotation 신택스를 만든다. 다음 코드는 Authenticated 유형의 주석을 banking으로 설정된 permission 속성과 함께 붙인다:

declare annotation : * Account.*(..)                    : @Authenticated(permission="banking");

@AspectJ pointcut은 같은 선언이 다음과 같이 작성되는 곳에서 @DeclareAnnotation 사용하여 같은 기능을 지원한다:

@DeclareAnnotation("* Account.*(..)")@Authenticated(permission="banking") void bankAccountMethods();

JBoss AOP의 경우, XML 스타일의 aspect를 사용할 때 annotation-introduction 엘리먼트를 사용하여 주석을 붙인다. 주석이 런타임시 보유되지 않는다면 invisible 속성이 지시한다. (표준 자바 메타데이터의 RetentionPolicy.SOURCE에 해당).

<annotation-introduction expr="method(* Account->*(..))"                         invisible="false">      @Authenticated(permission="banking")</annotation-introduction>

주석 공급의 원리는 신택스가 다르더라도 다양한 AOP 시스템 두루 비슷하다.

메타데이터를 이용한 AOP 디자인

메타데이터와 AOP를 결합하는 것은 매우 단순하다. 중요한 것은 메타데이터 기반 크로스커팅을 어디에 적용하고 어디에 적용하지 않아야 하는지를 아는 것이다. join point의 임의의 속성들을 사용하는 AOP 구현에서 메타데이터 기반의 pointcut을 결합하는 것 까지 시스템이 어떻게 진화하는지를 설명하는 것으로 위 질문에 대한 답을 하겠다. Part 2에서는 메타데이터 중심의 접근 방식을 선택할 때의 고려 사항들을 자세히 설명하겠다.

이 섹션의 논의는 두 가지 측면에서 유용하다. 우선, 메타데이터를 사용하는 것이 언제나 AOP 사용자들의 우선순위는 아니라는 것을 이해해야 한다. 둘째, 이 글의 예제 구현은 메타데이터 기반의 크로스커팅을 적용하기로 결정했을 때 디자인을 개발하는 방법에 대한 가이드라인일 뿐이다.

이 예제는 트랜잭션 관리 프로그램이다. AspectJ를 사용하여 예제의 모든 코드를 개발한 반면 다른 AOP 시스템들의 구현은 개념적으로 동일하다. 예제의 각 단계들은 원래 디자인의 리팩토링으로 간주하라. 목표는 시스템을 서서히 디커플(decouple)하여 모듈성을 향상시키는 것이다.

Version 1: 순진한 aspect

크로스커팅 문제를 모듈화하는 내 첫 번째 시도는 pointcut 정의 뿐만 아니라 pointcut에 대한 어드바이스를 포함하고 있는 시스템 스팩의 aspect를 적용하는 것이다. 이것은 매우 간단한 스키마이고 AOP를 배울 때 처음 만나게 되는 디자인이기도 하다. 그림 1은 하나의 aspect를 적용하는 디자인의 스키마이다:

그림 1. AOP를 사용하여 트랜잭션 관리를 구현하기

Listing 1은 위 디자인을 구현한 것이다:

Listing 1: 은행 시스템을 위핸 트랜잭션 관리 aspect
public aspect BankingTxMgmt {    pointcut transactedOps()         : execution(void Customer.setAddress(..))          || execution(void Customer.addAccount(..))          || execution(void Customer.removeAccount(..))          || execution(void Account.credit(..))          || execution(void Account.debit(..));              Object around() : transactedOps() {         try {             beginTransaction();             Object result = proceed();             endTransaction();             return result;         } catch (Exception ex) {             rollbackTransaction();             return null;         }    }        ... implementation of beginTransaction() etc.}

이 스키마는 기반 시스템에 대한 지식이 별로 요구되지 않는 aspect에 잘 맞는다. 예를 들어, 풀링(pooling)을 실행하고자 한다면 풀링되고 있는 리소스의 생성과 소멸에 대한 호출을 권고하는 제너릭 aspect를 작성할 수 있다. 하지만 제너릭 방식으로는 필요한 join point를 잡을 수 없는 크로스커팅 기능의 경우 이 방식은 한계가 있다. 우선, pointcut 정의가 시스템 스팩이기 때문에 aspect는 재사용 될 수 없다. 둘째, 시스템을 변경하면 aspect도 변경해야 한다. 다시 말해서, 첫 번째 버전의 시스템은 프로그램 엘리먼트들 간 N to 1 커플링과 pointcut을 남긴다. 이것이 최상의 옵션이 아니기 때문에 다른 시도가 필요하다.

Version 2: 재사용 가능한 aspect

내 두 번째 시도는 aspect를 재사용 할 수 있는 것으로 향상시켰다. aspect의 재사용 부분을 추출하여 시스템 스팩의 방식으로 pointcut을 정의하는 하위 aspect를 추가했다. 그림 2는 기본 aspect가 추출된 후의 구조이다:

그림 2. 재사용 할 수 있는 트랜잭션 관리 aspect 추출하기

Listing 2는 재사용이 가능한 기본 aspect이다. Listing 1과 비교해 보면 두 가지 변화를 알 수 있다: aspect는 추상적인 것으로 표시되고 transactedOps() pointcut은 정의가 제거된 채로 추상적인 것으로 표시된다:

Listing 2. 재사용 가능한 트랜잭션 관리 기본 aspect
public abstract aspect TxMgmt {    public abstract pointcut transactedOps();               Object around() : transactedOps() {         try {             beginTransaction();             Object result = proceed();             commitTransaction();             return result;         } catch (Exception ex) {             rollbackTransaction();             return null;         }    }       ... implementation of beginTransaction() etc.}

다음에, 기본 aspect에 대한 하위 aspect를 작성해야 한다. 아래 하위 aspect는 트랜잭션 관리 지원을 필요로 하는 join point를 캡쳐하는 pointcut을 정의한다. Listing 3은 Listing 2의 TxMgmt aspect를 확장한 은행 스팩의 하위 aspect이다. 하위 aspect는 transactedOps() pointcut을 Listing 1에 쓰였던 것과 똑 같은 것으로 정의한다.

Listing 3. 시스템 스팩의 하위 aspect
public aspect BankingTxMgmt extends TxMgmt {    public pointcut transactedOps()         : execution(void Customer.setAddress(..))          || execution(void Customer.addAccount(..))          || execution(void Customer.removeAccount(..))          || execution(void Account.credit(..))          || execution(void Account.debit(..));}

이것은 향상되기는 했지만 이 디자인 역시 하위 aspect와 클래스들 간 N-to-1 의존형이다. 은행 시스템에 필요한 트랜잭션 요구 사항들이 변경되면 BankingTxMgmt의 pointcut 정의도 변경되어야 한다. 이상적이지 못하다.

Version 3: Participant 패턴

위에서 재사용성의 문제를 다루었지만 N-to-1 의존성은 여전히 남아있다. Participant 패턴을 적용하여 이를 의존성을 제거할 수 있다. 전체 시스템에 하나의 하위 aspect를 사용하는 대신 많은 하위 aspect를 사용하면서—하위 시스템 당 하나— 비교적 안정적인 pointcut을 작성할 수 있다. 이 상황에서 하위 시스템은 패키지, 패키지 세트, 심지어는 클래스가 될 수 있다. 그림 3은 다른 엘리먼트들 간 구조 관계이다:

그림 3. participant 디자인 패턴 적용하기

Listing 4는 임베디드 클래스를 위한 pointcut을 정의하는 participant aspect를 가진 Customer 클래스이다.

Listing 4. 임베디드 participant aspect를 가진 Customer 클래스
public class Customer {    public void setAddress(Address addr) {        ...    }    public void addAccount(Account acc) {        ...    }    public void removeAccount(Account acc) {        ...    }    ...    private static aspect TxMgmtParticipant extends TxMgmt {        public pointcut transactedOps()             : execution(void Customer.setAddress(..))              || execution(void Customer.addAccount(..))              || execution(void Customer.removeAccount(..));    }}

Customer 클래스 예제에서 하위 aspect는 인자에 대한 와일드카드를 가진 모든 메소드들을 열거한다. 실제로 pointcut 정의를 단순화 하는데 와일드카드를 사용하게 될 것이다. 예를 들어 이 클래스의 모든 퍼블릭 메소드들을 다음 정의를 사용하여 transactedOps() pointcut으로 캡쳐되도록 선언할 수 있다:

public pointcut transactedOps()     : execution(public * Customer.*(..));

Listing 5에서, 시스템의 트랜잭션 관리 기능에 참여하기 위해 Account 클래스가 어떻게 하위 aspect를 삽입하는지를 보여주고 있다.

Listing 5. 삽입된 participant aspect를 가진 Account 클래스
public class Account {    public void credit(float amount) {        ...    }    public void debit(float amount) {        ...    }    public float getBalance() {        ...    }    ...    private static aspect TxMgmtParticipant extends TxMgmt {        public pointcut transactedOps()             : execution(void Account.credit(..))              || execution(void Account.debit(..));    }}

Customer 클래스처럼, 단계를 추가하면 pointcut을 단순화 할 수 있다. 예를 들어, getBalance() 메소드를 제외한 모든 퍼블릭 메소드들이 트랜잭션 관리 시 실행되어야 한다는 것을 깨달았다면? 이러한 깨달음을 적용하여 pointcut을 정의할 수 있었다:

public pointcut transactedOps()    : execution(public void Account.*(..))     && !execution(float Account.getBalance());

클래스가 변경되면 이 클래스에서 중첩된 하위 aspect만 변경해야 한다. 큰 N 대신 시스템 커플링을 각 하위 aspect에서 캡쳐 된 더 적은 프로그램 엘리먼트의 수인 n-to-1으로 시스템 커플링을 줄였다. 더욱이 트랜잭션 관리 필요성과 관련하여 클래스가 변경되면 삽입된 participant aspect의 pointcut만 변경하여 지역성을 보존한다.

이 예제는 AOP 관련 논의에서 종종 놓치는 중요한 부분을 설명하고 있다. 전체 시스템에 대한 서명 패턴을 찾으려 한다면 불쾌한 상황 즉, 불안정하고 복잡하며 부정확한 pointcut을 만나게 된다. 이 시스템의 하위 세트를 고려할 때 그 하위 시스템과 잘 작동되는 신호 패턴을 종종 찾을 수 있다. Participant 패턴을 클래스 당 하나의 aspect와 함께 사용하는 것은, 하위 시스템으로의 논리적 구분은 잘 이루어 지겠지만, 클래스를 하위 시스템으로 간주하는 것이다.

이 솔루션은 대부분의 상황에서 적용된다. 단점은 클래스들이 기본 aspect에 대한 직접적인 의존성이 있어서 기본 aspect가 언제나 시스템에 나타나야 한다는 점이다. 이 솔루션의 또 다른 문제는, 클래스가 중첩된 하위 aspect들을 삽입하여 협업에 명확히 참여하지 않는 한, 이것의 크로스커팅 기능이 적용되지 않는 다는 것이다. 이 문제는 솔루션 보다는 크로스커팅 기능의 본질을 다루어야 하는 문제인 것이다.

Version 4: 메타데이터 기반 pointcut

각 메소드를 변경하여 주석을 갖게 하고 시스템 당 하나의 하위 aspect가 되도록 할 예정이다.(Version 2와 유사) 하지만 나의 하위 aspect는 메타데이터 기반의 pointcut을 사용하여 필요한 join point를 잡을 것이다—메소드는 내가 공급한 주석을 실행할 것이다. 하위 aspect 자체는 시스템에 걸쳐 재사용될 것이다. 그림 4는 이 버전의 스키마이다.

그림 4. 메타데이터 기반 트랜잭션 관리

메타데이터 기반의 하위 aspect를 사용할 때, 클래스의 join point가 그 특성을 변경하면 해당 join point에 대한 주석만 변경되어야 한다. Listing 6는 Version 2에서 TxMgmt aspect를 확장한 하위 aspect이고 Transactional 유형의 주석을 수행하는 모든 메소드를 잡아서 transactedOps() pointcut을 정의한다.

Listing 6. 메타데이터 기반 트랜잭션 관리 하위 aspect
public aspect MetadataDrivenTxMgmt extends TxMgmt {    public pointcut transactedOps()         : execution(@Transactional * *.*(..));}

이 클래스는 Transactional 유형의 주석을 트랜잭션 관리 시 실행되어야 하는 모든 메소드에 첨부함으로서 하위 aspect와 협업해야 한다. Listing 7은 이 Customer 클래스를 주석을 수행하는 메소드와 함께 구현한 것이다:

Listing 7. Customer 클래스와 주석
public class Customer {    @Transactional    public void setAddress(Address addr) {        ...    }    @Transactional    public void addAccount(Account acc) {        ...    }    @Transactional    public void removeAccount(Account acc) {        ...    }    ...}

Listing 8은 유사한 Account클래스의 구현이다:

Listing 8. Account 클래스와 주석
public class Account {    @Transactional    public void credit(float amount) {        ...    }    @Transactional    public void debit(float amount) {        ...    }    public float getBalance() {        ...    }    ...}

이 지점에서 메소드와 협업 aspect 간 일대일 의존성을 확립했다. 또한 aspect와 클래스 간 직접적인 의존성도 제거했다. 결과적으로 기본 aspect만 변경하고자 할 때 시스템에 변경을 하지 않고도 이를 수행할 수 있다.

기본 aspect의 사용은 선택적이다. (계층을 무너뜨릴 수 있다.) 하지만 기본 aspect를 메타데이터 기반의 하위 aspect에서 분리하는 것은 몇 가지 장점이 있다. 우선, 분리된 aspect는 주석 유형을 선택할 수 있다. 한 시스템에서 주석 유형으로 Transactional을 사용하여 join point를 잡을 수 있다. 이때 다른 시스템의 주석 유형은 Tx가 될 수 있다. 둘째, Participant 패턴과 메타데이터 기반의 방식 간 선택을 분리된 aspect로 나아간다. 셋째, 이 방식을 사용하여 @Purchase 또는 @OrderProcessing같은 비지니스 주석들에서 트랜잭션 pointcut을 분리할 수 있다. 마지막으로 메타데이터 기반의 방식과 Participant 패턴 기반의 방식을 결합할 수 있다.

주석을 통해 협업함으로서 참여에 대한 책임은 (참여하는 하위 aspect 대신) 개별 메소드로 옮겨간다. MetadataDrivenTxMgmt와 클래스들 간 의존성은 주석 유형과 관련 문법으로 제한된다.

반복보다 좋은 것은 없다. 하지만 한 가지 특별한 시나리오가 있다. 최적의 결과를 위해 한 단계 더 들어간 디자인을 리팩토링 할 수 있는 시나리오이다.

Version 5: 메타데이터 공급자로서의 Aspect

특정 상황에서, 클래스 내 대부분의 메소드들은 (Version 4의 주석 같은) 주석을 수행해야 한다. 더욱이 많은 크로스커팅 특성들은 메소드 당 한 개 이상의 주석을 필요로 한다. 이 상황은 모든 메소드에 대해 선언된 많은 주석들을 주석 지옥으로 설명되는 현상으로 몰고 간다. 주석 아수라장은 Participant 패턴과 주석자-공급자 aspect를 결합함으로서 줄일 수 있다. 이는 참여하는 join point를 표현하는 분명한 방식이 있을 때 유용한 옵션이다. 이 같은 경우, 주석자-공급자 디자인은 join point에 대해 주석을 놓치는 위험을 피한다.

그림 5. 메타데이터 공급자로서의 Aspect

주석자 aspect는 한 개 이상의 declare annotation들을 사용한다: 예를 들어, declare annotation : <Method pattern> : <Annotation definition>;. >;. 이 예제에서, 클래스에 Participant 패턴과 비슷한 협업과 하나의 주석자 aspect를 사용했다. 하지만 이렇게 하는 것이 이 디자인의 근본적인 요구사항은 아니다. 예를 들어 패키지 당 하나의 주석자 aspect를 구현할 수 있다. 핵심 개념은 필요한 join point를 캡쳐하고 그 클래스의 주석 아수라장 현상을 피하는 명확한 서명 패턴 또는 동적 콘텍스트 정보(제어-흐름 등)를 가진 알맞은 하위 시스템을 찾는 것이다. Listing 9는 주석자 aspect를 가진 Customer 클래스 이다.

Listing 9. 삽입된 주석자 aspect를 가진 Customer 클래스
public class Customer { public void setAddress(Address addr) { ... } public void addAccount(Account acc) { ... } public void removeAccount(Account acc) { ... } ... private static aspect Annotator { declare annotation: public Customer.*(..): @Transactional; } }

Listing 10의 Account 클래스도 이와 비슷하다.

Listing 10. 삽입된 주석자 aspect를 가진 Account 클래스
public class Account {    public void credit(float amount) {        ...    }    public void debit(float amount) {        ...    }    ...    private static aspect Annotator {        declare annotation: public Account.*(..): @Transactional;    }}

이제 이 구현과 Version 3에서 Participant 패턴을 사용하는 구현을 비교해보자. 이 버전은 한 가지 중대한 단점이 있다: 이것은 클래스를 특정 aspect로 묶는다. 이는 매우 적극적인 개입이다—기본 aspect는 언제나 존재해야 한다. (이는 모든 참여 aspect에게 있어 기본 aspect이기 때문이다.) 주석자 aspect 접근방식을 사용하여 참여는 공통으로 이해된 주석 유형의 레벨에서만 발생할 수 있다.

다리 역할을 하는 주석 유형들
다양한 기술들이 비지니스 목적을 위해 사용되는 주석들과 aspect 구현에 사용되는 주석들 간 다리로서 사용하고 있다. 예를 들어, @Purchase@OrderProcessing 주석을 가진 모든 메소드들이 트랜잭션 관리가 되어야 한다는 것을 알고 있다면 Listing 11 처럼 aspect를 작성할 수 있다.

Listing 11. 비지니스 주석을 크로스커팅 주석으로 변환하기
public aspect BusinessTransactionBridge {    declare annotation: @Purchase *.*(..): @Transactional;    declare annotation: @OrderProcessing  *.*(..): @Transactional;}

이 aspect는 @Transactional 주석을 @Purchase 또는 @OrderProcessing 주석을 가진 모든 메소드에 붙인다. 이 방식은 Listing 2와 Listing 6을 결합하여, 트랜잭션 관리 로직으로 메소드를 실행한다.

결론

메타데이터는 프로그램 엘리먼트에 대한 추가 정보를 표현하는 하나의 방식이다. 자바의 새로운 메타데이터 장치는 유형화된 주석을 사용하도록 한다. 메타데이터 사용법은 매우 간단하다. 어떤 것을 사용하는 지는 본인의 선택에 달려있다. Aspect 지향 프로그래밍은 메타데이터의 기본적인 소비자이다. 안정적인 서명 기반의 pointcut으로 쉽게 지정되지 않는 크로스커팅 문제에 대해 보다 단순한 pointcut을 사용하여 메타데이터로 증가된 join point 모델은 AOP를 보다 접근 가능한 것으로 만든다.

이 글에서 메타데이터 개념을 설명하고 AOP에서 프로그램 엘리먼트에 대한 메타데이터에 저장된 정보를 사용하는 방법을 설명했다. 또한 다양한 AOP 시스템에 대한 메타데이터 기반의 pointcut을 지원하는데 어떤 방식들이 개입되어 있는지 설명했다. 다섯 가지의 디자인 리팩토링을 통해 AOP 시스템에 메타데이터를 적용하는 방법 또한 제시했다.

다음 글에서는 AOP를 소비자와 공급자로서 메타데이터를 정의하고 사용하는 디자인 고려 사항들을 자세히 설명하겠다. AOP 시스템에서 메타데이터를 결합하는 것이 망각 원리에 어떤 영향을 끼치는 지와 AOP 시스템의 채택에는 어떤 영향이 있는지도 설명하겠다. 다차원 작업 공간에서 신호로서 AOP에 접근하는 고급 방식도 설명할 것이다.

참고자료

AOP@Work.
"Annotations in Tiger, Part 1: 메타데이터를 자바 코드에 추가하기"
• AOP 실행: AspectJAspectWerkzJBoss AOP
Spring AOP
proposed language modifications
"Aspect-Oriented Programming and JBoss
"Aspect-Oriented Annotations"
TestNG , "TestNG로 자바 단위 테스트를 쉽게!"
Barter
Contract4J
"Metadata for language extensions"
AspectJ in Action: Practical Aspect-Oriented Programming
Aspect-Oriented Software Development
Mastering AspectJ: Aspect-Oriented Programming in Java
Eclipse AspectJ
AspectJ Cookbook
Java technology zone
Developer Bookstore,
Java- related titles
Java technology zone tutorials page, developerWorks

제공 : DB포탈사이트 DBguide.net

출처명 : 한국IBM
Posted by tornado
|

요청 쿼리에 ?lzt=html 를 붙이니까 없어짐..

 

http://192.168.1.53:8080/lps-3.0/my-apps/hello1.lzx?lzt=html

http://192.168.1.53:8080/lps-3.0/my-apps/hello1.lzx?lzt=html-object

 

이런식으로 호출함..

 

라즐로 이용해서 포토 게시판 만들면 끝장이겠는걸~

'JAVA > JSP_Servlet' 카테고리의 다른 글

[펌] myeclipse에서 xdoclet 설명  (0) 2005.10.04
[펌] 웹개발문서모음 - iHelpers  (0) 2005.08.24
jwebunit  (0) 2005.06.03
OSCache / Quartz ... 등등 좋은것들..  (0) 2005.06.03
[펌] Opensource Web Grid  (0) 2005.05.30
Posted by tornado
|

http://kr.sun.com/developers/j2se/techtip.html

 

한동안 안봤더니... 많이 올라왔넹..

Posted by tornado
|

jwebunit

JAVA/JSP_Servlet 2005. 6. 3. 11:24
Posted by tornado
|

http://www.opensymphony.com/

 

볼꺼는 많고... 음주가무는 끊이지 않고... 고민...

 

'JAVA > JSP_Servlet' 카테고리의 다른 글

[openlaszlo] 아래에 푸터 붙어있을때...  (0) 2005.07.05
jwebunit  (0) 2005.06.03
[펌] Opensource Web Grid  (0) 2005.05.30
[펌] TD 를 에디트 하기 ,,,  (0) 2005.05.13
Tyrex 이용한 jndi 이용방법..  (2) 2005.05.10
Posted by tornado
|
JMX (Java Management Extensions) Introduce
 
JMx는 프로그래머들에게 자바 어플리케이션의 모니터링과 관리 기능을 제공한다. 실제로 이 API는 웹서버에서 네트워크 디바이스, 웹폰에 이르기까지 자바로 이용가능한 것은 어느 것이든 로컬 혹은 원격으로 처리 할 수 있게 한다.
 
DownLoad
http://java.sun.com/products/JavaManagement/download.html 에서 다운로드 받을 수 있다. 현재 최종 버전은 다음과 같다.
 
l         JMX Remote API 1.0.1_03 Reference Implementation(JSR 3)
l        JMX 1.2.1 Reference Implementation(JSR 160)
 
JMS 환경
l        Instrumentation Tier(JSR 3에 포함) : 어플리케이션, 디바이스, 서비스와 같은 리소스들은 MBean(Managed Bean)이라고 불리는 자바 오브젝트를 이용하여 설치된다. MBean은 원격으로 관리하고 모니터링하는 JMS 에이전트를 통해 속성과 연산으로 구성된 관리 인터페이스를 보여준다.
l        Agent Tier(JSR 3에 포함) : JMS 에이전트의 주요 컴포넌트는 MBean서버이며, 이는  MBean이 등록되는 코어관리 에이전트 오브젝트 서버이다. JMX에이전트는 또한 MBean을 핸들링하기 위한 서비스들을 포함하고 있다. JMX에이전트는 직접적으로 리소스를 제어하고 관리가 가능하도록 한다.
l        Remote Management Tier(JSR 160에 부분 포함) : 이 티어는 에이전트 JVM외부의 원격 관리 어플리케이션으로부터 접근 가능한 JMX에이전트를 만드는 프로토롤 어덥터와 커넥터를 정의 한다. 커넥터는 원격 클라이언트 JMX-aware이고 로컬 클라이언트가 보게될 것과 같은 JMX API를 볼 때 사용된다. 어덥터는 원격 클라이언트가 SNMP(Simple Network Management Protocol) CIM/WBEM(Common Infromation Model and Web Base Enterprise Management)같은 일반 관리 프로토콜을 이용할 때 사용된다.
 
Developer Type
l        리소스 관리 MBean 개발 : JMX 기술은 관리를 위해 보여지는 인터페이스를 정의 한다.
l        에이전트를 생성하고 배치하는 개발
n         MBean 서버를 생성하거나 플랫폼에 의해 제공되는 것을 사용한다.
n         MBean 네이밍 컨베션을 이용하여 리소스를 나타내는 MBean을 등록한다.
n         플랫폼(RMI/SNMP)에 의해 공급된 커넥터와 프로토콜 어뎁터를 형성하거나, 리소스가 원격으로 접근되었을 때는 커스텀 커넥터나 어덥터를 추가한다.
l        원격 매니저를 작성하는 개발 : JMX 에이전트와 상호작용하는 커넥터나 프로토콜을 선택하고, 나타난 MBean을 통해 원격으로 관리되는 리소스의 뷰를 구축한다.
 
MBean 4가지 타입
l        Standard MBean : 표준MBean MBean 가장 일반적인 타입이다. 대부분의 개발자는 외의 MBean타입을 생성할 필요가 없다.
l        Dynamic MBean : introspection 통하는 대신 javax.management.DynamicMBean 인터페이스를 이용하여 관리 인터페이스를 프로그램적으로 구현한다. 이를 위해서는 관리를 위해 나타나는 속성과 작용을 표현하는 정보 클래스에 의존한다. Dynamic MBean MBean 관리 인터페이스가 컴파일시 알려지지 않았을 -예를 들어, XML파일을 파싱하는데 결정되었을 -종종 사용된다.
l        Model MBean : 일반적이고 형성가능한 MBean으로써 어플리케이션이 어떤 리소스든지 다이나믹하게 설치하는 사용한다(API에서 Modle MBean Class Dynamic MBean Class 상속 받는다.). 본질적으로, 관리 인터페이스와 실제 리소스가 프로그램적으로 설정되도록 구현되어온 것이 다이나믹 MBean이다. 이로 인해 자바 다이나믹 관리 에이전트에 연결된 어떤 매니저라도 MBean모델을 다이나믹하게 실증하고 형성할 있다.
l        Open MBean : 오픈 MBean 데이터 타입에 특정 제한을 가진 다이나믹 MBean이며, 이는 관리 어플리케이션과 이들의 관리자들이 런터임시 발견되는 새로운 관리 오브젝트들을 이해하고 사용할 있게 한다. 오픈 MBean JMX스펙에 따르는 넓은 범위의 어플리케이션에 오픈될 필요가 있는 리소스를 설치하는데 있어 유동적인 수단을 제공한다.
 
Sample Program
샘플 프로그램은 JDK1.5.0_03 버전에서 테스트 하였습니다. 1.5 이하 버전에서는 구동되지 않습니다.
 
샘플은 RSS 서버스를 Reader하는 어플리케이션(rssReaderAgent) 관리하는 MBean(rssJMX) 구성하였다. MBean RSS Reader 읽기 횟수, 읽어온 등록들의 총수, 현재 읽어온 글의 수을 모니터링하며 reset()메서드는 위의 수를 초기화 한다. MBean 구성인 rssJMX, rssJMXMBean, rssJMXDevice 같은 이름으로 시작해야 한다.(rssJMX) 샘플을 구동하기 위해 RSS UTIL API 필요하다. 해당 API 아래에서 다운 받을 있다.
Posted by tornado
|
JMS API
 
ConnectionFactory
Message Receiver MOM과 연결하기 위해서 Connection 객체를 필요로 한다. ConnectionFactory객체는 Connection에 관한 정보를 가지고 있으면서 Connection객체를 생성할 수 있는 Interface이다.
 
ConnectionFactory 객체의 2가지 종류
 
-         PTP에서 사용하는 QueueConnectionFactory
-         PUb/Sub방식에서 사용하는 TopicConnectionFactory이다.
 
각각의 객체는 JNDI Name으로 등록되며, Program Code에서는 다음과 같이 JNDI Name을 이용하여 찾을 수 있다.
 
QueueConnectionFactory qConnFactory =
(QueueConnectionFactory)context.lookup("QueueConnectionFactory");
 
ConnectionFactory에는 createXXXConnection()이라는 method가 존재하는데, 이를 이용하여 Connection객체를 생성할 수 있다.
 
QueueConnection qConn=qConnFactory.createQueueConnection();
 
Destination
통신Program에서 일반적으로 접속, Connection이 일어나려면 IP Address Port Number가 필요하다. 하지만 Message System에서는 MOM을 제공하는 Vendor들마다 사용하는 주소방식이 상이하기 때문에 표준을 지정하기가 곤란한다. 따라서 MOM에서는 Destination이라는 객체를 사용하는데, 이것은 Sender Receiver Message를 주고 받을 곳을 지칭하는 가상 채널정도라고 말할 수 있다.(일반적으로 Destination 객체는 MOM자체를 지칭한다.) PTP에서는 Destination은 보통 Queue라고 불리며, Sub/Pub에서는 Topic이라고 불린다.
Program Code에서는 다음과 같이 Destination객체를 찾을 수 있다.
 
-         Queue queue=(Queue)ctx.lookup("myQueue");
-         Topic topic=(Topic)ctx.lookup("myTopic");
 
Connection
Connection객체는 Sender Receiver MOM과 연결해 주는 객체이다. 이 연결은 TCP/IP 형태로 이루어진다. 사실 Sender MOM에게 Message를 보내려면 Session이라는 객체가 필요한데, 이 객체는 바로 Connection객체를 통해서 얻어진다. Connection객체 역시 PTP QueueConnection TopicConnection 두 가지 종류가 있다. 사용된 Connection객체는 Program 종료 시 close()를 호출해서 닫아주어야 한다.
 
-         QueueConnection qConn = qConnFactory.createQueueConnection();
-         TopicConnection tConn = tConnFactory.createTopicConnection();
-         qConn.close();
-         tConn.close();
 
Session
Session Message를 생성(Produce)하고 소비(Consume)하기 위한 환경을 제공한다. 특히 Session MessageProducer MessageConsumer를 생성하며 Temporary Destination객체를 생성한다. Session에는 QueueSession TopicSession 두가지 종류가 있다. QueueSession을 생성하기 위해서는 QueueConnection을 이용해야 하고, TopicSession을 생성하기 위해서는 TopicSession을 생성해야 한다.
 
-         QueueSession qSession
        =qConn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
 
-         TopicSession tSession
        = tConn.createTopicSession(false.Session.AUTO_ACKNOWLEDGE);
 
사용된 첫 번째 Argument Session Transaction에 포함되는지 아닌지를 설정한다. 참고로 'true' Setting될 경우, 두 번째 Argument는 무시된다. 두 번째 Argument는 전달된 Message ACK를 제어하는 내용이다.
 
Message Producer
Message Destination객체에 전달하기 위해서는 Message Producer가 필요하다. PTP에서는 Message Producer QueueSender라고 부르며, Pub/Sub에서는 TopicPublisher라고 부른다.
QueueSession createSender()를 이용하면 QueueSender 객체를 생성할 수 있고, TopicSession createPublisher()를 이용하면 TopicPublisher 객체를 만들 수 있다.
 
-         QueueSender qSender = qSession.createSender(Queue);
 
-         TopicPublisher tPublisher = tSession.createPublisher(Topic);
 
이렇게 생성된 Message Producer를 통해 Message를 전송하기 위해서는 다음처럼 send() publish()를 이용한다.
-         qSender.send(Message);
 
-         tPublisher.publish(Message);
 
Message Consumer
Message Producer로 부터 생성되어 전해진 Message를 받기 위해서는 Message Consumer가 필요하다. PTP에서는 Message Consumer QueueReceiver라고 부르며, Pub/Sub에서는 TopicSubscriber라고 부른다. QueueSession createReceiver()를 이용하면 QueueReceiver 객체를 생성할 수 있고, TopicSession createSubscriber()를 이용하면 TopicSubscriber객체를 만들 수 있다. Message를 받는 방식에는 다음과 같은 동기식 방식과 비동기식 방식이 있다.
 
동기식 방식: 이 방식을 사용하는 Client Message를 소비하기 위해서 receiver()를 호출한다. receiver()가 호출되면 Client Message가 모두 도착하거나 Time-Out될 때까지 Block상태에 놓인다.
 
비동기식 방식 : 이 방식을 사용하는 Client Event Delegation Model을 사용하여 Event Handler를 등록할 수 있다. Event Handler MessageListener Interface implements해야 한다. Message가 목적지에 도착할 때 마다 onMessage()가 자동 호출된다.
 
다음은 Program Code에서 각각의 Message Consumer를 생성하는 예이다.
 
-         QueueReceiver qReceiver=qSession.createReceiver(Queue);
 
-         TopicSubscriber tSubscriber = tSession.createSubscriber(Topic);
 
이렇게 생성된 Message Consumer를 통해 Message를 전송 받기 위해서는 다음처럼 먼저 Connection객체가 가지고 있는 start()를 호출해야 한다. 그 후, QueueReceiver TopicSubscriber에 있는 receive()를 이용하면 Message를 받을 수 있다.
 
-        qConn.start();
-        TextMessage tMessage = (TextMessage) qReceiver.receive(1);
 
receive()의 첫 번째 Argument Time-Out을 의미한다.(1/1000 )
 
다음 Code는 비동기식 방식을 이용하여 Message를 소비한다. 다음의 Code처럼 TopicSubscriber 객체의 setMessageListener() 를 이용해 Event handler에게 Message소비를 위임한다.
 
-        tSubscriber.setMessageListener(listener);
 
Message 객체
Message 객체는 JMS에서 Application Program사이에 주고 받는 Data를 의미한다. 이같은 Message객체는 3부분으로 나눌 수 있다.
 
-         Header : JMS Client MOM Server에서 사용되는 통신정보를 가지고 있다.
 
-         Property : Option항목으로 JMS Client MOM Server의 고유한 정보가 포함되어 있다.
 
-         Body : Message의 내용이 포함되어 있다.
 
JMS에는 다음과 같이 크게 5가지 형태의 Message Type이 있다.
 
-         TextMessage : 문자열 Data로서 String Type이다.
-         MapMessage : 이름과 값의 쌍으로써, 이름은 String Type이고 값은 Primitive Type이다.
-         ByteMessage : Byte Stream Data Type
-         ObjectMessage : Serialize Object
-         Message : Body가 존재하지 않는 Message
 
Message는 오직 한번만 정확하게 전달되어야 한다.
 
JMS Message가 신뢰성을 가지고 전달될 수 있도록 다음과 같은 다양한 level을 지원한다.
-         Controlling Message Acknowledgement (Message ACK 제어)
-         Specifying Message Persistence (Message 지속성 기술)
-         Message Priority Level (Message 우선순위)
-         Message Validate Duration (Message 유효기간)
-         Temporary Destination (임시 목적지 사용)
Message ACK제어
MOM Client Message를 잘 받았다는 확인을 받은 후에야 Message소비가 성공적으로 끝났다고 판단한다. 성공적인 Message 소비는 다음과 같은 3단계를 거친다.
 
-         Client Message를 받는다
-         Client Message를 처리한다
-         MOM에게 Message를 잘 받았다는 확인 Message를 전송한다.
 
Transaction이 포함된 Session인 경우 Transaction Commit되면 확인 Message는 자동적으로 전달된다. 만약 Transaction Rollback되면 소비된 모든 Message는 다시 Client에게 전달된다. Transaction이 포함된 Session이 아닌 경우는 createQueueSession()이나 createTopicConnection() Argument로 어떤 값을 사용하느냐에 따라 확인 Message를 전달하는 방식이 달라진다.
 
-         AUTO_ACKNOWLEDGE : Client receive()를 성공적으로 수행하거나 MessageListener Message가 잘 처리된 경우 자동적으로 확인 Message가 전달된다.
-         CLIENT_ACKNOWLEDGE : Client가 인위적으로 acknowledge()를 호출해야 확인 Message가 전달된다.
-         DUPS_OK_ACKNOWLEDGE :확인 Message를 나중에 보낸다. 이 경우엔, 동일한 Message가 여러 번 전달될 수도 있다.
 
Message 지속성 기술
Message가 전달되는 도중 JMS가 중단될 수 있다. 이 경우, Message는 사라질 수도 있고 유지될 수도 있다. JMS API에는 이것을 기술하기 위한 2가지 mode를 제공한다.
 
-         PERSISTENT Delivery Mode : MOM MOM Service가 중단되는 경우, Message가 실종되지 않도록 한다. 이 모드는 Default속성이다.
-         NON_PERSISTENT Delivery Mode : PERSISTENT Delivery Mode와 반대되는 속성이다.
 
이같은 mode를 설정할 수 있는 방법에는 2가지가 있다.
-         MessageProducer Interface setDeliveryMode()를 이용한다.
-         send() publish() Argument를 이용한다.
 
Message 우선순위
중요한 Message나 급한 Message를 먼저 처리할 수 있도록 우선순위를 지정할 수 있다. 우선 순위는 0~9까지로 10단계로 되어 있으며, 0이 가장 낮은 우선순위이고, 9가 가장 높은 우선 순위이다. 4순위가 기본 순위이다. 우선순위를 지정하는 방법에는 2가지가 있다.
-         send() publish()에 우선순위를 기술한다.
-         MessageProducer 객체의 setPriority()를 이용한다.
Posted by tornado
|
JMS PTP Test Program 작성
 
HelloWorldJmsSender.java
import javax.naming.*;
import javax.jms.*;
import java.util.Properties;
 
public class HelloWorldJmsSender{
  public final static String JMS_FACTORY="QueueConnectionFactory";
  public final static String QUEUE="ExampleQueue";
 
  public static void main(String args[]) {
    try {
      QueueConnectionFactory queueConnectionFactory;
      QueueConnection queueConnection;
      QueueSession queueSession;
      QueueSender queueSender;
      Queue queue;
      TextMessage msg;
 
      // JNDI InitialContext 작성합니다.
 
      Properties env = new Properties();
      env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "jeus.jndi.JEUSContextFactory");
      env.setProperty(Context.PROVIDER_URL, "localhost");
      InitialContext ctx = new InitialContext(env);
 
     // Connection Factory Queue Look up 합니다
      queueConnectionFactory  = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
      queue = (Queue) ctx.lookup(QUEUE);
 
      // connection 작성
      queueConnection = queueConnectionFactory.createQueueConnection();
      // 세션을 작성
      queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
      // sender 작성
      queueSender   = queueSession.createSender(queue);
      // 메세지를 작성
      msg     = queueSession.createTextMessage();
      msg.setText("Hello World hurukku!!");
      // 메세지의 배송을 스타트
      queueConnection.start();
      // 메세지를 송신
      queueSender  .send(msg);
      // 접속을 close
      queueSender.close();
      queueSession.close();
      queueConnection.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
 
HelloWorldJmsReceiver.java
import javax.jms.*;
import javax.naming.*;
import java.util.Properties;
 
public class HelloWorldJmsReceiver {
  public final static String JMS_FACTORY="QueueConnectionFactory";
  public final static String QUEUE="ExampleQueue";
 
  public static void main(String[] args) {
    try {
      QueueConnectionFactory queueConnectionFactory;
      QueueConnection queueConnection;
      QueueSession queueSession;
      QueueReceiver queueReceiver;
      Queue queue;
      TextMessage msg;
 
      // JNDI InitialContext 작성합니다
 
      Properties env = new Properties();
      env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "jeus.jndi.JEUSContextFactory");
      env.setProperty(Context.PROVIDER_URL, "localhost");
      InitialContext ctx = new InitialContext(env);
 
      // Connection Factory Queue Look up 합니다
      queueConnectionFactory  = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
      queue = (Queue) ctx.lookup(QUEUE);
 
      // connection 작성
      queueConnection = queueConnectionFactory.createQueueConnection();
      // 세션을 작성
      queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
      // 리시버를 작성
      queueReceiver = queueSession.createReceiver(queue);
      // 메세지의 배송을 스타트
      queueConnection.start();
      // 메세지의 수신
      while (true) {
        Message m = queueReceiver.receive(1);
        if (m != null) {
          if (m instanceof TextMessage) {
            msg = (TextMessage) m;
            System.out.println(msg.getText());
          } else {
            break;
          }
        }
      }
      // 접속을 close
      queueReceiver.close();
      queueSession.close();
      queueConnection.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
 
Source Compile 및 실행
 
-         Compile
“javac -classpath /home/craftlee/jeus/lib/system/jeus.jar *.java”
 
-         Sender 실행
“java -classpath .:/home/craftlee/jeus/lib/system/jeus.jar:/home/craftlee/jeus/lib/system/jmxri.jar HelloWorldJmsSender”
 
jmsadmin에서 엔진모니터링
 
Destination Type : Queue
Name of Destination : ExampleQueue
JNDI name of Destination : ExampleQueue
 
Client information that is consuming this destination
-----------------------------------------------------
No client is consuming this destination.
 
number of messages remained in Queue : 1
 
-         Receiver 실행
“java -classpath .:/home/craftlee/jeus/lib/system/jeus.jar:/home/craftlee/jeus/lib/system/jmxri.jar HelloWorldJmsReceiver”
 
Destination Type : Queue
Name of Destination : ExampleQueue
JNDI name of Destination : ExampleQueue
 
Client information that is consuming this destination
-----------------------------------------------------
client 0 :      Address; 127.0.0.1, port; 34001
                clientNumber; 0
 
number of messages remained in Queue : 0
Posted by tornado
|
JMS pub/sub Test Program 작성
 
HelloWorldJmsPublisher.java
import javax.naming.*;
import javax.jms.*;
import java.util.Properties;
 
public class HelloWorldJmsPublisher {
  public final static String JMS_FACTORY="TopicConnectionFactory";
  public final static String TOPIC="ExampleTopic";
 
  public static void main(String[] args) {
    try {
      TopicConnectionFactory topicConnectionFactory;
      TopicConnection topicConnection;
      TopicSession topicSession;
      TopicPublisher topicPublisher;
      Topic topic;
      TextMessage msg;
 
      // JNDI InitialContext 작성합니다
      Properties env = new Properties();
      env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "jeus.jndi.JEUSContextFactory");
      env.setProperty(Context.PROVIDER_URL, "localhost");
      InitialContext ctx = new InitialContext(env);
 
      // Connection Factory Topic Look up 합니다
      topicConnectionFactory = (TopicConnectionFactory) ctx.lookup(JMS_FACTORY);
      topic = (Topic) ctx.lookup(TOPIC);
      // connection 작성
      topicConnection = topicConnectionFactory.createTopicConnection();
      // 세션을 작성
      topicSession = topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
      // 토픽 발행자를 작성
      topicPublisher = topicSession.createPublisher(topic);
      // 메세지를 작성
      msg = topicSession.createTextMessage();
      msg.setText("Hell World hurukku Topic JMS");
      // 메세지를 송신
      topicPublisher.publish(msg);
      // 접속을 닫습니다
      topicPublisher.close();
      topicSession.close();
      topicConnection.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
 
HelloWorldJmsSubscriber.java
import javax.naming.*;
import javax.jms.*;
import java.util.Properties;
 
public class HelloWorldJmsSubscriber {
  public final static String JMS_FACTORY="TopicConnectionFactory";
  public final static String TOPIC="ExampleTopic";
 
  public static void main(String[] args) {
    try {
      TopicConnectionFactory topicConnectionFactory;
      TopicConnection topicConnection;
      TopicSession topicSession;
      TopicSubscriber topicSubscriber;
      Topic topic;
      TextMessage msg;
 
      // JNDI InitialContext 작성합니다
      Properties env = new Properties();
      env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "jeus.jndi.JEUSContextFactory");
      env.setProperty(Context.PROVIDER_URL, "localhost");
      InitialContext ctx = new InitialContext(env);
 
      // Connection Factory Topic Look up 합니다
      topicConnectionFactory = (TopicConnectionFactory) ctx.lookup(JMS_FACTORY);
      topic = (Topic) ctx.lookup(TOPIC);
      // connection 작성
      topicConnection = topicConnectionFactory.createTopicConnection();
      // 세션을 작성
      topicSession = topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
      // 트픽크사브스크라이바를 작성
      topicSubscriber = topicSession.createSubscriber(topic);
      // 메세지의 배송을 스타트
      topicConnection.start();
      // 메세지의 수신
      msg = (TextMessage) topicSubscriber.receive();
      System.out.println(msg.getText());
      // 접속을 닫습니다
      topicSubscriber.close();
      topicSession.close();
      topicConnection.close();
    } catch ( Exception e ) {
      e.printStackTrace();
    }
  }
}
 
Source Compile 및 실행
 
-         Compile
“javac -classpath /home/craftlee/jeus/lib/system/jeus.jar *.java”
 
-         Subscriber 실행
“java -classpath .:/home/craftlee/jeus/lib/system/jeus.jar:/home/craftlee/jeus/lib/system/jmxri.jar HelloWorldJmsSubscriber”
 
-         Publisher 실행 (새로운 창으로 실행)
“java -classpath .:/home/craftlee/jeus/lib/system/jeus.jar:/home/craftlee/jeus/lib/system/jmxri.jar HelloWorldJmsPublisher”
Posted by tornado
|
Test Program 개발을 위한 환경 설정
1.       JEUS4.2 설치
(JEUS
http://technet.tmax.co.kr 에서 받으실 수 있습니다.)
2.       JEUSJMS 설정
A.        JEUSMain.xml 파일에 JMS 엔진 설정
<engine-command>
<type>jms</type>
        <name>engine</name>
        <startup-mode>xml</startup-mode>
</engine-command>
B.        JEUSMain.xml 파일 위치에 ‘host_name’_jms_engine 디렉토리 생성
C.       ‘host_name’_jms_engine 디렉토리 밑에 다음과 같은 JMSMain.xml 파일 생성
<?xml version="1.0"?>
<!DOCTYPE jms-server PUBLIC "-//Tmax Soft, Inc.//DTD JMS Main Config 4.0//EN"
                            "http://www.tmaxsoft.com/jeus/dtd/4.0/jms-main-config.dtd">
 
<jms-server>
    <error-log>
        <level>debug</level>
    </error-log>
    <access-log>
        <target>stdout</target>
    </access-log>
 
    <connection-factory>
        <type>queue</type>
        <name>QueueConnectionFactory</name>
        <export-name>QueueConnectionFactory</export-name>
            <error-log>
            <target>stdout</target>
            <level>debug</level>
        </error-log>
    </connection-factory>
    <connection-factory>
        <type>Topic</type>
        <name>TopicConnectionFactory</name>
        <export-name>TopicConnectionFactory</export-name>
        <error-log>
            <target>stdout</target>
            <level>debug</level>
        </error-log>
    </connection-factory>
    <destination>
        <type>Queue</type>
        <name>ExampleQueue</name>
        <export-name>ExampleQueue</export-name>
    </destination>
    <destination>
        <type>Topic</type>
        <name>ExampleTopic</name>
        <export-name>ExampleTopic</export-name>
    </destination>
</jms-server>
 
D.        JEUS 구동 및 JMS 엔진 실행 확인
sh> jmsadmin ‘host_name’_jms_engine -Uadministrator –P설치시비밀번호
hurukku_jms_engine>serverall
[2005.02.02 12:51:44][2] ExampleTopic have NonDurableSubscribers of 0 and DurableSubscribers of 0
[2005.02.02 12:51:44][2] total message count:0
[2005.02.02 12:51:44][2] [NonDurableSubscriber] total consumers:0
 
number of current client : 0
peak of number of client : 0
total number of thread in server : 5
number of running thread in server : 0
total memory in server JVM : 6225920
free memory in server JVM : 2908104
Specific threadpool information :
         poolName : JMSServer    isDaemon: true
         allowIncrease: false    reserveExcess: true
         increaseFactor: 3       prepareInitial: true
         initial_thread_count: 5
         idleTime_forNotInitial: 120000  idleTime_forInitial: 0
         maximum_size: 10        threadId: 5
         pool_size: 5    worksTodo: 0
 
Type of ErrorLog : CONSOLE
Level of ErrorLog : DEBUG
BufferSize of ErrorLog : 4096
Period to change ErrorLog File (day) : -1
 
Type of AccessLog : CONSOLE
BufferSize of AccessLog : 4096
Period to change AccessLog File (day) : No Change File
 
Client information
------------------
No client is connected to Server
 
ConnectionFactory information
-----------------------------
  [1]
Name of ConnectionFactory : QueueConnectionFactory
JNDI name of ConnectionFactory : QueueConnectionFactory
ClientID of ConnectionFactory : null
MaxSessionThreadNumber of ConnectionFactory : 5
 
  [2]
Name of ConnectionFactory : TopicConnectionFactory
JNDI name of ConnectionFactory : TopicConnectionFactory
ClientID of ConnectionFactory : null
MaxSessionThreadNumber of ConnectionFactory : 5
 
 
Destination information
-----------------------
  [1]
Destination Type : Queue
Name of Destination : ExampleQueue
JNDI name of Destination : ExampleQueue
 
Client information that is consuming this destination
-----------------------------------------------------
No client is consuming this destination.
 
number of messages remained in Queue : 0
 
  [2]
Destination Type : Topic
Name of Destination : ExampleTopic
JNDI name of Destination : ExampleTopic
 
Client information that is consuming this destination
-----------------------------------------------------
No client is consuming this destination.
 
number of messages remained in Topic : 0
 
DurableSubscriber information that is registered to this destination
--------------------------------------------------------------------
No Durable Subscriber is registered this destination.
Posted by tornado
|
Is JMS?
 
JMS(Java Message System)는 비동기적 의미를 내포하고 있는 UDP 통신개념의 MailSystem과 유사하지만 Component간 통신이라는 점에서 다르다. JMS의 비동기적 메세지 전송은 아래 그림과 같이 MOM(Message Oriented Middleware)이 중간에서 Message를 받아두었다가 Receiver가 접속을 하면 Message를 전달해 주는 방식으로 이루어진다.



[Message System
architecture]
 
Messaging System의 경우 JMS API뿐 아니라 C와 같은 Non-Java AP를 위한 Lib Code를 제공하는 경우가 있는데 이런 경우에는 Java Application이 아닌 다른 언어로 개발된 Application과도 호환이 되기 때문에, JMS기반의 Messaging 시스템은 Mainframe이나 다른 Application들과 Java Application을 연동하는데 많이 사용이 되며, 근래에 많이 출시되는 EAI솔루션도 JMS를 이용하는 경우가 많다.
 
Feature of JMS
AP A AP B가 통신을 한다고 했을때, 통신하는 방법은 방식에 따라 data-centric이냐, interface-centric이냐로 분리될 수 있다. RMI, IIOP, SOAP 또는 직접 TCP Packet을 정의하여 socket으로 통신하는 방식은 interface-centric이라고 한다. 데이타를 보내는 Sender에서 데이타를 보내게 되고, receiver는 어떤 데이타 형이 올지를 알고 있다. (데이타 타입이 서로 약속되어 있다.) 그리고 sender recevier로 부터 어떤 데이타를 받을 것인지를 알고 있고, 그 데이타가 올때까지 기다리는 게 일반적인 흐름이다. 즉 서로 통신을 하는 AP들이 데이터 형이나 동작 방법에 대해서 알고 있는 경우가 된다. 그러나 JMS의 경우 data-centric 모델로, sender는 데이타를 보내기만 한다. Receiver data를 받았건 말건, 내지는 receiver의 수가 얼마가 되었는지는 상관하지 않는다. JMS에서는 sender JMS Messaging System에 데이타를 보내기만 한다. Receiver는 이렇게 Messaing System에 보내진 데이타를 중계해서 받는다. 이때 데이타 형은 미리 정해져 있지 않다. (물론 데이타를 받은 다음에는 알맞은 형으로 casting해야 된다. 그러나 데이타형을 몰라도 데이타를 받는 것 자체에는 문제가 없다.) 즉 메세지를 주고 받는데에 sender receiver간의 약속이 필요하지 않다.
 
Asynchronous messaging
기본적으로 JMS Messaging 시스템은 Async 방식의 Messaging을 지원한다. Interface-centric의경우, data send하면 ack를 받거나 return 값을 받을 때까지 sender waiting을 하게 된다. Receiver역시, 계속 sender로 부터 데이타가 오기를 기다린다. 이런 통신 방식은 sync 방식이라 한다. 그러나 Async방식은 sender는 일단 message 시스템에 데이타를 보낸다. Receiver가 받았는지 여부를 확인할 필요 없이, sender는 계속해서 메세지를 보내고, 메세지를 다 보냈으면 작업을 중단한다. Receiver sender로 부터 데이타가 오기만을 기다리는게 아니라, 필요할 때, message 시스템에 저장되어 있는 (sender로 부터 보내진) 메시지만 꺼내서 바로 사용하면 된다. sender receiver message 전송작업이 동시에 일어나지 않게 된다.
 
Reliable and unreliable messaging
앞에서 설명했듯이 JMS sender receiver의 상태에 상관없이 message를 무조건 보낸다고 설명했다. 여기서 집고 넘어가야 할 것이 그럼 어떻게 sender가 보낸 메시지가 receiver에 도착했는지를 보장할 수 있느냐는 것이다. (네트워크 문제나 기타 문제로 메세지가 유실 될 수 있다. ) 이건 JMS Messaging 시스템이 보장해준다. Reliable Messaging의 경우, sender가 데이타를 보냈으면 시스템이 중간에 다운되더라도, sender가 보낸 메세지는 receiver에게 도착할 수 있도록 보장한다. 이런 Reliable Messaging은 회사간 거래에서 중요한 데이타를 전송하는 모델 등에유용하게 사용될 수 있다. 그 밖에 실시간 데이타 처럼 신뢰성이 중요하지 않고 메세지가 전송되는 것이 중요하다면 unreliable Messaging을 이용하여, 별도의 보장 없이 메세지를 빨리 전송하는데 만 초점을 맞출 수 있다.
 
Message System에는 크게 2가지 방식
 
1.      PTP(Peer-To-Peer)


[PTP Message System Architecture]
 
PTP방식은 Sender Receiver Queue를 이용해서 Message를 주고 받는 형태이다. 또한 이 방식은 Sender가 보내는 Message는 오로지 한 Receiver만 받게 되어 있다. 그림처럼 Messaging Server Queue를 가지고 있어 Sender가 보낸 Message를 보관하고 있고, Receiver는 원하는 시간에 Message를 가져갈 수 있다. 이 때, ReceiverMessage Server에게 Message를 잘 받았다는 ACK신호를 보낸다.
 
2.      Pub/Sub (Publisher-Subscriber)방식



[Sub/Pub Message System Architecture]
 
Pub/Sub 방식은 Message를 받고 싶어하는 Subscriber들은 원하는 Topic에 등록한다. 그림처럼 PublisherMessaging Server가 가지고 있는 해당 Topic Message를 보내며, Messaging Server Topic에 등록되어 있는 Subscriber들에게 Message를 전달하는 구조이다.
Posted by tornado
|

출처 : LoveLazur

 

Opensource Web Grid

1) 대략 javascript아니면 DHTML [javascript라면 이것도 OK]
http://www.activewidgets.com/documentation/tutorial/grid/data-jsarray.htm

하지만 이렇게 작성도 가능함 [tag lib version]
http://www.activewidgets.com/messages/1694-8.htm



2) http://www.codeproject.com/useritems/DBGrid.asp
순수 jsp타입에서 접근하기엔 이게 좋아보인다.

2-2) sand box에서는 이런식으로 component화 되어있음 tablib임.
http://jakarta.apache.org/taglibs/sandbox/doc/datagrid-doc/index.html


2-3) 개인적으로 이게 좋은데... 단점이 없지 않음
http://displaytag.sourceforge.net/

 

 

------ 좋다 ... 이중에 맨 마지막거. -----

'JAVA > JSP_Servlet' 카테고리의 다른 글

jwebunit  (0) 2005.06.03
OSCache / Quartz ... 등등 좋은것들..  (0) 2005.06.03
[펌] TD 를 에디트 하기 ,,,  (0) 2005.05.13
Tyrex 이용한 jndi 이용방법..  (2) 2005.05.10
에혀... 문서를 자세히 봐야해...  (0) 2005.05.03
Posted by tornado
|

예전에 어디다 적어놨던거 같은데... 찾으니 없어서 다시 적음 -.-;

간단한 것이지만 까먹고 나면 문서 찾아봐야 하고 해서 ...


그럼 본문 시작.......


java.util.Collections 클래스에는 sort(List list) 라는 정적 메소드와

sort(List list, Comparator c) 라는 메서드가 있다.


이넘들을 이용해서 객체를 정렬 할 수 있다..

문제는 사용자 정의 객체(대표적으로 Java Bean) 들은 정렬대상에서 제외 된다.

예를 들면..


class TestBean{
 int pk;
 String name;

 public void setPk(int pk){ this.pk = pk; }
 public int getPk(){ return pk; }
 public void setName(String name){ this.name = name; }
 public String getName(){ return name; }

 public String toString(){
  return "[pk = " + pk + " , name = " + name + "]";
 }

}


이런 클래스가 하나 있다.

이 클래스에는 pk 라는 변수가 있는데.. 이 번호를 기준으로 객체를 정렬하고 싶다.

main 메소드를 만들어 정렬을 수행해 보자..

import java.util.*;

public class CompareToTest{
 public static void main(String[] args){
 
  System.out.println("정렬정렬 김정렬..");
  List list = new ArrayList();

  int[] arr = new int[]{8,3,4,6,1,2,7,5,9};
  String[] name = new String[]{"a", "z","y", "k","l","o","t","q", "f"};

  TestBean bean = null;
  for(int i = 0; i < arr.length; i++){
   bean = new TestBean();
   bean.setPk(arr[i]);
   bean.setName(name[i]);

   list.add(bean);
  }

  System.out.println("정렬 전");

  for(int i = 0; i < list.size(); i++){
   bean = (TestBean)list.get(i);
   System.out.println(bean);
  }

  Collections.sort(list);  // <-- 여기서 정렬...

  System.out.println("정렬 후");
  for(int i = 0; i < list.size(); i++){
   bean = (TestBean)list.get(i);
   System.out.println(bean);
  }
 }
}


이 클래스를 실행하면...


정렬정렬 김정렬..
정렬 전
[pk = 8 , name = a]
[pk = 3 , name = z]
[pk = 4 , name = y]
[pk = 6 , name = k]
[pk = 1 , name = l]
[pk = 2 , name = o]
[pk = 7 , name = t]
[pk = 5 , name = q]
[pk = 9 , name = f]
java.lang.ClassCastException
 at java.util.Arrays.mergeSort(Arrays.java:1152)
 at java.util.Arrays.mergeSort(Arrays.java:1163)
 at java.util.Arrays.sort(Arrays.java:1079)
 at java.util.Collections.sort(Collections.java:113)
 at CompareToTest.main(CompareToTest.java:28)
Exception in thread "main"


이런 에러가 발생하게 된다..

에러가 발생하는 건 당연한 이야기 이다.

Collections.sort() 메서드에서 무슨 근거로.. List 안에 들어있는 객체들을 정렬하겠냐 말이쥐..

그럼 이걸 정렬되게 하려면 어떻게 하면 되나...


java.lang 패키지에 있는 Comparable 인터페이스를 구현하면 된다.


java.lang.Comparable 인터페이스에는 compareTo(Object o) 라는 메서드가 있다.

이 녀석을 재정의 하면 된다..

재정의 규칙은 다음과 같다.


o.pk 가 현재 PK 보다 크다면  1 을 반납

o.pk 가 현재 pk 보다 작다면 -1 을 반납

o.pk 가 현재 pk 와 같다면 0 을 반납.


이러면 모든게 끝난다.


그럼 수정한 예를 보자.

class TestBean implements Comparable{
 int pk;
 String name;

 public void setPk(int pk){ this.pk = pk; }
 public int getPk(){ return pk; }
 public void setName(String name){ this.name = name; }
 public String getName(){ return name; }

 public String toString(){
  return "[pk = " + pk + " , name = " + name + "]";
 }


 // 구현 메소드
 public int compareTo(Object o){
  TestBean bean = (TestBean)o;
 
  if(bean.pk < this.pk) return 1;
  else if(bean.pk > this.pk) return -1;
  else return 0;
 }
}


이 예제를 실행하면 아래와 같이 나올것이다..


정렬정렬 김정렬..
정렬 전
[pk = 8 , name = a]
[pk = 3 , name = z]
[pk = 4 , name = y]
[pk = 6 , name = k]
[pk = 1 , name = l]
[pk = 2 , name = o]
[pk = 7 , name = t]
[pk = 5 , name = q]
[pk = 9 , name = f]
정렬 후
[pk = 1 , name = l]
[pk = 2 , name = o]
[pk = 3 , name = z]
[pk = 4 , name = y]
[pk = 5 , name = q]
[pk = 6 , name = k]
[pk = 7 , name = t]
[pk = 8 , name = a]
[pk = 9 , name = f]


Collections.sort(List list) 외에 Arrays.sort(Object[] a) 도 같은 결과가 나온다.


TestBean 의 내부변수인 pk 로 정렬을 해보았다..

이 상태에서... name 으로 비교를 해 볼수도 있다.

방법은 아주 단순하고 간단하다.

바로 java.util.Comparator 인터페이스를 상속하여.. 새로운 클래스를 구현하고 그 클래스를 정렬하는 곳에 알려주면 된다.


java.util.Comparator 인터페이스에는 구현해야할 메소드가 두 개 있다.

public int compare(Object o1, Object o2)

public boolean equals(Object obj)


이렇게 두개만 구현해 주면 된다..


그럼 이름으로 정렬하기 위해 새로운 클래스를 하나 만들어보자.

class MyComparator implements Comparator{
 public int compare(Object o1, Object o2){
  return ((TestBean)o1).getName().compareTo(((TestBean)o2).getName());
 }
 
 public boolean equals(Object o){
  return true;
 }
}

그리고 테스트를 위해 main 메서드의 내부를 아래와 같이 수정했다.

import java.util.*;

public class CompareToTest{
 public static void main(String[] args){
 
  System.out.println("정렬정렬 김정렬..");
  List list = new ArrayList();

  int[] arr = new int[]{8,3,4,6,1,2,7,5,9};
  String[] name = new String[]{"a", "z","y", "k","l","o","t","q", "f"};

  TestBean bean = null;
  for(int i = 0; i < arr.length; i++){
   bean = new TestBean();
   bean.setPk(arr[i]);
   bean.setName(name[i]);

   list.add(bean);
  }

  //-------------------------
  // 추가된 부분
  //-------------------------
  TestBean temp = new TestBean();
  temp.setPk(1);
  temp.setName("m");
  list.add(temp);


  System.out.println("정렬 전");

  for(int i = 0; i < list.size(); i++){
   bean = (TestBean)list.get(i);
   System.out.println(bean);
  }

  Collections.sort(list);

  System.out.println("정렬 후");
  for(int i = 0; i < list.size(); i++){
   bean = (TestBean)list.get(i);
   System.out.println(bean);
  }

  //-------------------------
  // 추가된 부분
  //-------------------------

  Collections.sort(list, new MyComparator()); 

  System.out.println("Comparator 구현 후");
  for(int i = 0; i < list.size(); i++){
   bean = (TestBean)list.get(i);
   System.out.println(bean);
  }
 }
}



이 클래스를 실행하면 아래와 같은 결과가 나온다...


정렬정렬 김정렬..
정렬 전
[pk = 8 , name = a]
[pk = 3 , name = z]
[pk = 4 , name = y]
[pk = 6 , name = k]
[pk = 1 , name = l]
[pk = 2 , name = o]
[pk = 7 , name = t]
[pk = 5 , name = q]
[pk = 9 , name = f]
[pk = 1 , name = m]
정렬 후
[pk = 1 , name = l]
[pk = 1 , name = m]
[pk = 2 , name = o]
[pk = 3 , name = z]
[pk = 4 , name = y]
[pk = 5 , name = q]
[pk = 6 , name = k]
[pk = 7 , name = t]
[pk = 8 , name = a]
[pk = 9 , name = f]
Comparator 구현 후
[pk = 8 , name = a]
[pk = 9 , name = f]
[pk = 6 , name = k]
[pk = 1 , name = l]
[pk = 1 , name = m]

[pk = 2 , name = o]
[pk = 5 , name = q]
[pk = 7 , name = t]
[pk = 4 , name = y]
[pk = 3 , name = z]



훔.. 말주변이 없어서 더이상 설명이 안되네.....

머 두개의 클래스(TestBean 과 MyComparator ) 를 합쳐서 지지고 볶구 해도 되겠징.. ^^;

또는 보기 좋게~ static 메서드로 만들어도 되겠고..

또는...

또는...

또는...

또는....

또는......


머리 깨지네 ㅋ

Posted by tornado
|
Posted by tornado
|