달력

32024  이전 다음

  • 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
[url] http://www.ibm.com/developerworks/kr/library/ws-pojo-springcxf/


소개

이번 글에서는 CXF와 스프링을 사용해 주문 처리 웹 서비스를 구축하고 개발한다. 이 웹 서비스는 고객에게 주문을 받아 처리하고, 유효한지 점검해 유일한 주문 ID를 반환한다. 이 글을 읽은 후에 웹 서비스를 구축하고, 개발하는 데 CXF의 개념과 특징을 적용할 수 있을 것이다.

시스템 요구사항

이번 글에 나오는 예제를 실행하려면 컴퓨터에 다음 소프트웨어가 설치, 구성되어 있는지 확인해야 한다.

  • 자바(Java) 5 이상
  • 톰캣(Tomcat) 5 이상
  • 앤트(Ant) 빌드 툴
  • CXF 바이너리 배포판 2.1

위 배포판을 설치한 다음에는 다음 환경 변수를 구성하자.

  • JAVA_HOME(자바)
  • CATALINA_HOME(톰캣)
  • ANT_HOME(앤트)
  • CXF_HOME(CXF)

예제를 위해 CXF_HOME=C:\apache-cxf-2.1로 설정하고, PATH 환경 변수에 다음 내용을 추가하자.

  • JAVA_HOME\bin
  • CATALINA_HOME\bin
  • ANT_HOME\bin

왜 CXF일까?

아파치 CXF(Apache CXF)는 웹 서비스를 편리하게 구축하고, 개발하는 데 필요한 견고한 기반을 제공하는 오픈 소스 프레임워크다. 이를 이용해 성능과 확장성이 높은 서비스를 만들 수 있다. 만든 서비스는 JBoss, IBM® WebSphere®나 BEA WebLogic 톰캣과 같은 좀 더 진보된 서버 인프라뿐만 아니라 톰캣과 스프링 기반 경량급 컨테이너에 배포할 수도 있다.

특징

프레임워크는 다음과 같은 특징을 제공한다.

  • 웹 서비스 표준 지원: CXF는 다음 웹 서비스 표준을 지원한다.
    • JAX-WS(Java API for XML Web Services)
    • SOAP
    • WSDL(Web Services Description Language)
    • MTOM(Message Transmission Optimization Mechanism)
    • WS-Basic Profile
    • WS-Addressing
    • WS-Policy
    • WS-ReliableMessaging
    • WS-Security
  • 프론트엔드 모델링(front-end modeling): CXF는 다양한 프론트엔드 API를 사용해 웹 서비스를 생성하도록 해주는 프론트엔드 모델링 개념을 제공한다. 이 API는 간단히 팩토리 빈(factory beans)을 사용하고, JAX-WAS 구현을 통해 웹 서비스를 생성하도록 해준다. 또한 동적 웹 서비스 클라이언트도 생성할 수 있게 해준다.
  • 도구 지원: CXF는 자바 빈, 웹 서비스, WSDL 간에 변환을 해주는 다양한 도구를 제공한다. 메이븐(Maven)과 앤트(Ant)에 대한 통합 지원 기능을 제공하며, 스프링과의 자연스러운(seamlessly) 통합도 지원한다.
  • RESTful 서비스 지원: CXF는 RESTful(Representational State Transfer) 개념을 지원하며, 자바 플랫폼에 대한 JAX-RS 구현을 지원한다(이번 연재의 Part 2에서 RESTful 서비스에 대한 더 많은 정보를 제공하겠다).
  • 다양한 전송과 바인딩(binding) 지원: CXF는 XML에서 CSV(Comma Separated Value)에 이르기까지 여러 종류의 전송을 지원한다. 또한 JAXB(Java Architecture for XML Binding)와 SOAP과 HTTP 프로토콜 바인딩 외에 AEGIS 데이터 바인딩도 지원한다.
  • XML 이외의 바인딩 지원: CXF는 JSON(JavaScript Object Notation)과 CORBA(Common Object Request Broker Architecture) 같은 XML 이외의 바인딩을 지원한다. JBI(Java Business Integration) 아키텍처와 SCA(Service Component Architecture) 또한 지원한다.

웹 서비스 개발하기

JAX-WS 프론트엔드를 사용해 주문 처리 웹 서비스를 만들어 이를 스프링 빈(bean)으로 등록하는 방법을 확실하게 살펴보자. 자바 클래스를 먼저 개발하고, 이 클래스에 웹 서비스를 의미하는 애노테이션을 붙이는 코드 우선 접근법을 사용한다. 이를 위해서는 대체로 다음 단계를 따르면 된다.

  1. 서비스 종단 인터페이스(SEI: Service Endpoint Interface)를 만들고 웹 서비스로 노출되는 메서드를 정의한다.
  2. 구현 클래스를 만들고 웹 서비스 애노테이션을 붙인다.
  3. beans.xml을 만들고 JAX-WS 프론트엔드를 사용해 스프링 빈으로 서비스 클래스를 정의한다.
  4. 스프링과 CXF를 통합하는 web.xml을 만든다.

먼저 주문 처리 웹 서비스 SEI를 만들어보자.

주문 처리 웹 서비스 SEI 만들기

OrderProcess라는 이름으로 SEI를 만들자. 이 SEI는 order 빈을 받아 문자열을 반환하는 processOrder 메서드를 갖게 된다.processOrder 메서드의 목적은 고객에게 주문을 받아 유일한 주문 ID를 반환하도록 처리하는 것이다.


Listing 1. OrderProcess SEI
                
package demo.order;

import javax.jws.WebService;

@WebService
public interface OrderProcess {
  String processOrder(Order order);
}

Listing 1에서 볼 수 있는 것처럼, OrderProcess SEI는 웹 서비스 애노테이션이 붙은 단순한 표준 자바 인터페이스다. @WebService 애노테이션은 간단하게 인터페이스를 웹 서비스 인터페이스로 만들어준다. OrderProcess SEI는 하나의 processOrder 메서드를 갖는다. 이 메서드는 매개변수로 Order를 받아 문자열로 주문 ID를 반환한다.


Listing 2. OrderProcess 서비스 구현
                
package demo.order;

import javax.jws.WebService;

@WebService(endpointInterface = "demo.order.OrderProcess")
public class OrderProcessImpl implements OrderProcess {

 public String processOrder(Order order) {
  return order.validate();
 }
}

SEI 구현 작성하기

이전 절에서 SEI 구현을 작성하면서 구현 클래스인 OrderProcessImpl이 웹 서비스가 되도록 애노테이션을 더해 붙였고, 이전 단계에서 만들었던 SEI의 완전한 클래스 이름(fully qualified name)을 값으로 갖는 endpointInterface 속성을 제공했다. 이 값은 이 클래스가OrderProcess SEI를 구현했다는 것을 의미한다. SEI를 구현했으므로 주문 ID를 반환하는 processOrder 메서드 구현을 제공해야 한다.

우리는 SEI와 SEI의 구현체를 만들었다. 이제 CXF를 사용해 JAX-WS 프론트엔드를 사용하는 실제 서비스 컴포넌트를 만들 수 있다.


Listing 3. beans.xml 환경 구성 파일
                
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:jaxws="http://cxf.apache.org/jaxws"
 xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

 <import resource="classpath:META-INF/cxf/cxf.xml" />
 <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
 <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> 

 <jaxws:endpoint 
  id="orderProcess" 
  implementor="demo.order.OrderProcessImpl" 
  address="/OrderProcess" />
	  
</beans>

CXF의 환경 구성 파일 생성하기

CXF 환경 구성 파일은 실제로 빈 정의를 포함하는 스프링 환경 구성 파일이다. JAX-WS 프론트엔드 환경 구성을 사용해 OrderProcess웹 서비스에 대한 빈 정의를 만들자. beans.xml에 나오는 <jaxws:endpoint> 태그는 OrderProcess 웹 서비스를 JAX-WS 엔드포인트로 지정해준다. 이 웹 서비스를 공개(publish)하는 데 CXF가 내부적으로 JAX-WS를 사용하는 건 매우 효과적인 방법이다.<jaxws:endpoint>에 구현 클래스 이름인 OrderProcessImpl과 주소를 제공해야 한다. 여기서 제공하는 주소는 웹 컨텍스트(web context)와 관련 있다.


Listing 4. web.xml 환경 구성 파일
                
<web-app>
 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>WEB-INF/beans.xml</param-value>
 </context-param>

 <listener>
  <listener-class>
   org.springframework.web.context.ContextLoaderListener
  </listener-class>
 </listener>

 <servlet>
  <servlet-name>CXFServlet</servlet-name>
  <display-name>CXF Servlet</display-name>
  <servlet-class>
   org.apache.cxf.transport.servlet.CXFServlet
  </servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>

 <servlet-mapping>
  <servlet-name>CXFServlet</servlet-name>
  <url-pattern>/*</url-pattern>
 </servlet-mapping>
</web-app>

마지막으로 다음 작업을 해야 한다.

  • CXF 환경 구성 파일을 불러오는 web.xml 파일 만들기
  • 환경 구성 파일을 불러오는 스프링 컨텍스트 로더(context loader) 사용하기
  • 클라이언트 프로그램에서 오는 모든 요청을 제어하는 CXFServlet 등록하기

이제 서버측 컴포넌트가 필요로 하는 개발은 대충 마무리됐다. 이제 OrderProcess 서비스에 요청을 보내는 클라이언트 컴포넌트를 개발할 수 있다.

클라이언트 개발하기

Listing 5에서 봤던 것처럼, 서버 종단 지점을 쉽게 만들 수 있는 것처럼 클라이언트 빈을 만드는 것도 매우 쉽다. JaxWsProxyFactoryOrderProcess 웹 서비스에 대한 클라이언트 빈을 만드는데 사용된다. 팩토리 빈은 서비스 클래스(OrderProcess)와 서비스의 URL을 요구한다. 그러므로 클라이언트 빈 스텁(stub)이 되는 OrderProcess는 팩토리 빈 참조를 사용해 생성된다.


Listing 5. client-bean.xml 클라이언트 웹 환경 구성 파일
                
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:jaxws="http://cxf.apache.org/jaxws"
 xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/jaxws 
http://cxf.apache.org/schema/jaxws.xsd">

 <bean id="client" class="demo.order.OrderProcess" 
  factory-bean="clientFactory" factory-method="create"/>
	
 <bean id="clientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
  <property name="serviceClass" value="demo.order.OrderProcess"/>
  <property name="address" value="http://localhost:8080/orderapp/OrderProcess"/>
 </bean>
	  
</beans>

스프링 컨텍스트(context)를 사용해 정의한 클라이언트 빈을 받아온 다음, processOrder 메서드를 호출하는 자바 메인 프로그램을 만들어보자.


Listing 6. 클라이언트 코드
                
public final class Client {

 public Client() {
 }

 public static void main(String args[]) throws Exception {
  ClassPathXmlApplicationContext context 
   = new ClassPathXmlApplicationContext(new String[] 
     {"demo/order/client/client-beans.xml"});

  OrderProcess client = (OrderProcess)context.getBean("client");
    Order order = new Order();

  String orderID = client.processOrder(order);
  System.out.println("Order ID: " + orderID);
  System.exit(0);
 }
}

프로그램 실행하기

프로그램을 실행하기 전에, 루트 C:\ 폴더 밑에 그림 1에서 보는 것처럼 디렉터리 구조를 만들고, 이번 글의 앞에서 다뤘던 컴포넌트를 넣어두자.

  • 자바 코드는 패키지 폴더에 포함
  • beans.xml과 web.xml은 web\web-inf 폴더에 포함
  • client-beans.xml은 demo\order\client 폴더에 포함

그림 1. 코드 디렉터리 구조
코드 디렉터리 구조 

OrderProcess 웹 서비스와 클라이언트를 구축하고, 배포하고, 실행하는 데 앤트(Ant) 도구를 사용한다. 코드는 톰캣 서버에 배포된다. c:\orderapp 폴더 아래 ant deploy 커맨드를 사용해 코드를 배포하자.

애플리케이션 폴더(c:\orderapp)는 앤트 빌드 파일을 갖고 있다. 위 명령을 실행한 후에 orderapp 코드는 orderapp.war 파일로 톰캣 서버 환경에 배포된다. 이제 CATALINA_HOME\bin 폴더에서 catalina start 명령을 입력해 톰캣 웹 서버를 시작하자.

톰캣의 webapps 폴더에 orderapp 폴더가 생성된다. 서버가 시작된 후에 ant client 명령을 입력해 애플리케이션을 시작하자. 주문 ID가 화면에 출력된다(그림 2).


그림 2. 프로그램이 출력한 화면
프로그램이 출력한 화면 

결론

이번 글에서는 간단한 CXF 프레임워크의 특징과 코드를 작성해야 하는 별다른 노력 없이도 웹 서비스를 만들 수 있는 방법을 설명했다. 빈 컨텍스트 파일을 사용하는 CXF와 스프링 통합에 대해서도 배웠다. 또한 프레임워크가 실제로 웹 서비스 인프라스트럭처 컴포넌트를 만드는 의미를 추상화한 방법과, 오로지 웹 서비스를 생성하는 데만 집중할 수 있도록 어떤 전문적이면서 간편한 셸 API를 제공하는지를 알아 봤다

이제 CXF를 사용하는 웹 서비스 생성의 기본을 살펴봤으니, Part 2에서는 CXF와 스프링을 사용해 RESTful 서비스를 POJO로 노출하는 방법을 살펴볼 생각이다.


Posted by tornado
|

출처 : http://wheelersoftware.com/articles/spring-cxf-web-services.html



1 | 2 | 3 | 4 | Next »
Software Development

Web Services with Spring 2.5 and Apache CXF 2.0

Quickly create web services in Spring 2.5 using Apache CXF 2.0, a.k.a. XFire 2.0.

In this tutorial I explain how to get a web service up and running using Spring 2.5 and Apache CXF 2.0, which is the combination of Celtix and XFire and is considered XFire 2.0. (I don't know what the Celtix team would say about that, but that's what the XFire site says.) Here I just treat the web service itself; to learn about consuming the web service using Spring and CXF, please see the article Make Web Services Transparent with Spring 2.5 and Apache CXF 2.0.

CXF supports both WSDL-first and Java-first web service development. This article takes the Java-first approach.

Project Setup

The first thing you'll need to do is download CXF from the Apache CXF site. At the time of this writing the project is in incubator status and the latest version is 2.0.4. That's the version I'm using.

You'll also find it useful to know about the section of the CXF user documentation that deals with writing a service with Spring, but currently the docs describe how to integrate with Spring 2.0, and since I want to integrate with Spring 2.5, there are some differences worth highlighting along the way.

Also, the docs describe a "Hello, World" web service that just returns a string, and in this tutorial we want to go a little further than that and actually do a class databinding.

Here are the service-side dependencies you'll need. Inside the distribution there is a file called WHICH_JARS that describes in a little more detail what the JARs are for and which ones you'll really need, if you're interested in that. But the following is essentially the list given in the user docs.

CXF itself

  • cxf-2.0.4-incubator.jar

CXF dependencies

Note that for CXF 2.0.4 the documented dependencies are almost but not quite the same as the JARs that are actually included in the distribution, once again, at the time of this writing (February 2008). Specifically the stax, neethi and XMLSchema JARs are not the ones listed. Here's the corrected list for CXF 2.0.4:

  • commons-logging-1.1.jar
  • geronimo-activation_1.1_spec-1.0-M1.jar (or Sun's Activation jar)
  • geronimo-annotation_1.0_spec-1.1.jar (JSR 250)
  • geronimo-javamail_1.4_spec-1.0-M1.jar (or Sun's JavaMail jar)
  • geronimo-servlet_2.5_spec-1.1-M1.jar (or Sun's Servlet jar)
  • geronimo-stax-api_1.0_spec-1.0.jar
  • geronimo-ws-metadata_2.0_spec-1.1.1.jar (JSR 181)
  • jaxb-api-2.0.jar
  • jaxb-impl-2.0.5.jar
  • jaxws-api-2.0.jar
  • neethi-2.0.2.jar
  • saaj-api-1.3.jar
  • saaj-impl-1.3.jar
  • wsdl4j-1.6.1.jar
  • wstx-asl-3.2.1.jar
  • XmlSchema-1.3.2.jar
  • xml-resolver-1.2.jar

Aegis dependencies

In addition to the above, you will need to add jdom-1.0.jar since Aegis databinding uses it.

Spring dependencies

In this case ignore what's in the CXF documentation, because we're integrating with Spring 2.5 instead of Spring 2.0. I'm going to assume that you have Spring 2.5 already set up on your web services project, including Hibernate or anything else that your web services will need on the implementation side.

Just in case you were wondering, you don't need the Spring MVC module's DispatcherServlet as CXF comes with its own servlet, org.apache.cxf.transport.servlet.CXFServlet.

OK, that should be good for basic project setup. Let's write a simple service.


Posted by tornado
|
간단하게 되는군요

<page:applyDecorator name="theDecorator">
    <s:action name="footer" executeResult="true" />
</page:applyDecorator>
Posted by tornado
|
원문 : http://edocs.bea.com/wls/docs61/webServices/advanced.html


Invoking Web Services Without Using the WSDL File


This Appendix shows an example of a dynamic client application that does not use the WSDL file when it invokes a WebLogic Web Service. In particular, the example invokes a message-style Web service and sends data to WebLogic Server.

Dynamic client applications that do not use the WSDL of the Web service are dynamic in every way, because they can invoke a Web service without knowing either the interface of the Web service, or the JavaBean interface of return values and parameters, or even the number and signatures of the methods that make up the Web service.

The example uses the URL http://www.myHost.com:7001/msg/sendMsg to invoke the Web Service. Because the example shows a dynamic client application that does not use the WSDL of the Web service, the preceding URL is for the Web service itself, rather than the URL for the WSDL of the Web service.

The procedure after the example discusses relevant sections of the example as part of the basic steps you follow to create this client.

import java.util.Properties;
import java.net.URL;
import javax.naming.Context;
import javax.naming.InitialContext;
import weblogic.soap.WebServiceProxy;
import weblogic.soap.SoapMethod;
import weblogic.soap.SoapType;
import weblogic.soap.codec.CodecFactory;
import weblogic.soap.codec.SoapEncodingCodec;
import weblogic.soap.codec.LiteralCodec;
public class ProducerClient{
  public static void main( String[] arg ) throws Exception{
    CodecFactory factory = CodecFactory.newInstance();
    factory.register( new SoapEncodingCodec() );
    factory.register( new LiteralCodec() );
    WebServiceProxy proxy = WebServiceProxy.createService( 
       new URL( "http://www.myHost.com:7001/msg/sendMsg" ) );
    proxy.setCodecFactory( factory );
    proxy.setVerbose( true );
    SoapType param = new SoapType( "message", String.class );
    proxy.addMethod( "send", null, new SoapType[]{ param } ); 
    SoapMethod method = proxy.getMethod( "send" );
    String toSend = arg.length == 0 ? "No arg to send" : arg[0];
    Object result = method.invoke( new Object[]{ toSend } );
  } 
}

Follow these steps to create a dynamic Java client that does not use WSDL to invoke a message-style WebLogic Web Service that sends data to WebLogic Server:

  1. Get the Java client JAR file from the WebLogic Server hosting the WebLogic Web Service.

    For detailed information on this step, refer to Downloading the Java Client JAR File from the Web Services Home Page.

  2. Add the Java client JAR file to your CLASSPATH on your client computer.
  3. Create the client Java program. The following steps describe the Web services-specific Java code:

    1. In the main method of your client application, create a factory of encoding styles and register the two that are supported by WebLogic Server (the SOAP encoding style and Apache's Literal XML encoding style):
      CodecFactory factory = CodecFactory.newInstance();
      factory.register( new SoapEncodingCodec() );
      factory.register( new LiteralCodec() );
      
    2. Add the following Java code to create the connection to the Web service and set the encoding style factory:
      WebServiceProxy proxy = WebServiceProxy.createService( 
             new URL( "http://www.myHost.com:7001/msg/sendMsg" ) );
      proxy.setCodecFactory( factory );
      proxy.setVerbose( true );
      
    3. Add the following Java code to dynamically get the send method of the Web service:
       SoapType param = new SoapType( "message", String.class );
       proxy.addMethod( "send", null, new SoapType[]{ param } ); 
       SoapMethod method = proxy.getMethod( "send" );
      
    4. Invoke the send method and send data to the Web service. In the example, the client application simply takes its first argument and sends it as a String; if the user does not specify an argument specified, then the client application sends the string No arg to send:
      String toSend = arg.length == 0 ? "No arg to send" : arg[0];
      Object result = method.invoke( new Object[]{ toSend } );
      
  4. Compile and run the client Java program as usual.

The following more complex example shows how to use a send method that accepts a org.w3c.dom.Document, org.w3c.dom.DocumentFragment, or org.w3c.dom.Element data type as its parameter. The example shows how to set literal encoding on this flavor of the send method.

import java.util.Properties;
import java.net.URL;
import java.io.File;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import weblogic.apache.xml.serialize.OutputFormat;
import weblogic.apache.xml.serialize.XMLSerializer;
import weblogic.apache.xerces.dom.DocumentImpl;
import weblogic.soap.WebServiceProxy;
import weblogic.soap.SoapMethod;
import weblogic.soap.SoapType;
import weblogic.soap.codec.CodecFactory;
import weblogic.soap.codec.SoapEncodingCodec;
import weblogic.soap.codec.LiteralCodec;
public class ProducerClient{
  public static void main(String[] args) throws Exception{
    String url = "http://localhost:7001";
    // Parse the arguments list
    if (args.length != 2) {
      System.out.println("Usage: java examples.webservices.message.ProducerClient 
http://hostname:port \"message\"");
      return;
    } else if (args.length == 2) {
      url = args[0];
    }
    CodecFactory factory = CodecFactory.newInstance();
    factory.register(new SoapEncodingCodec());
    factory.register(new LiteralCodec());
    URL newURL = new URL(url + "/msg/sendMsg");
    WebServiceProxy proxy = WebServiceProxy.createService(newURL);
    proxy.setCodecFactory(factory);
    proxy.setVerbose(true);
    SoapType param = new SoapType( "message", Document.class );
    proxy.addMethod( "send", null, new SoapType[]{ param } );
    SoapMethod method = proxy.getMethod("send");
    // Print out proxy to make sure method signature looks good
    System.out.println("Proxy:"+proxy);
    DocumentBuilderFactory dbf =
                     DocumentBuilderFactory.newInstance();
    //Obtain an instance of a DocumentBuilder from the factory.
    DocumentBuilder db = dbf.newDocumentBuilder();
    //Parse the document.
    Document w3cDoc = db.parse(new File("/test/fdr_nodtd.xml"));
    //Class parserClass = Class.forName("org.jdom.adapters.XercesDOMAdapter");
    //DOMAdapter da = (DOMAdapter)parserClass.newInstance();
    //Document w3cDoc = da.getDocument(new File("/test/fdr_nodtd.xml"),false);
    // Print out XML just to make sure the document was read successfully
    OutputFormat of = new OutputFormat();
    of.setEncoding("UTF-8");
    of.setLineWidth(40);
    of.setIndent(4);
    XMLSerializer xs = new XMLSerializer(System.out,of);
    xs.serialize(w3cDoc);
    System.out.println("Before Invoke");
    Object result = method.invoke( new Object[]{w3cDoc} );
    System.out.println("Done");
  }
}
Posted by tornado
|

현재 프로젝트 weblogic 6.1 -.-;

웹서비스 작업해야 하는데, 6.1 에서는 Stateless Session Bean, Message Driven Bean 에만

웹서비스를 생성 할 수 있다.

ant task 중에서 wsgen 이라는게 있더군.

그래서 ant build 파일 만들어서 빌드 하고 ear 을 weblogic 에 심어줬다.

자바 클라이언트로는 dynamic, static 둘다 잘 됨.

그러나~~~~~

asp.net 2.0 에서 자바 웹서비스를 생성하지 못한다.

웹서비스 참조 걸었을 경우 map파일을 만들지 못함.

왜!!!!

웹로직에서 생성한 웹서비스의 wsdl.jsp 에 문제가 있다.

<definitions
targetNamespace="java:com.xxx"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="java:com.xxx"
>

xmlns 가 1999 라서 문제가 생긴다.

문제를 빨랑 해결해야 해서...

ear 파일 까고, war 파일 까서...

아래와 같이 고쳤다.

<definitions
targetNamespace="java:com.xxx"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="java:com.xxx"
>

년도만 2001 로 고쳤더니 잘 됨.

버젼이 낮으니 이런 문제도 발생을 하는군요.

Posted by tornado
|

OS 는 솔라리스이고, 메일서버는 sendmail
was 는 resin

메세지를 MimeMessage 로 선언하고 setContent 메서드를 이용해서

HTML 메세지를 넣었다.

허걱..

익셉션 발생...

왜 그러지?? 하고 쳐다보다가 문득 !! 예전에 웹 메일 만들던 생각이 남.

아~~ 맞다... 바디파트에 넣고 하면 되지~~

   Multipart multi = new MimeMultipart();    

   MimeBodyPart mbp = new MimeBodyPart();
  
   mbp.setContent("여기에 내용을 ~~~", "text/html; charset=KSC5601");
  
   multi.addBodyPart(mbp);
  
   msg.setContent(multi);

   Transport.send(msg);


해결...

메일링 보낼때 뭔가 찜찜해서 스레드로 백그라운드에서 메일 전송하게 함 ..

현재로서는 탄탄하게 잘 돌아감.

Posted by tornado
|
[펌] http://forum.java.sun.com/thread.jspa?threadID=247536&messageID=913161


i got the solution for the problem myself
if any one encounter same problem ,

Just change
session = javax.mail.Session.geDefaultInstance(props,auth);


to

session = javax.mail.Session.getInstance(props,auth);
while using in more than one smtps,user,passwords etc in same JRE
bye
Posted by tornado
|

http://xfire.codehaus.org/


Codehaus XFire is a next-generation java SOAP framework. Codehaus XFire makes service oriented development approachable through its easy to use API and support for standards. It is also highly performant since it is built on a low memory StAX based model.

Posted by tornado
|
  Enterprise Java Technologies Tech Tip에 오신 여러분을 환영합니다
Enterprise Java Technologies
테크팁
2006년 5월 4일자
  이번 호에서는,

» EJB 3.0을 이용하여 웹 서비스 개발하기
» 커스텀 빈을 이용하여 GlassFish 관리 시스템 확장하기

에 대해 다룹니다.

이 팁들은 Java EE 5의 오픈소스 구현(GlassFish)을 이용하여 개발되었으며, GlassFish 프로젝트 페이지에서 GlassFish를 다운로드 받으실 수 있습니다.

-샘플 아카이브 다운로드 받기-
EJB 3.0을 이용하여 웹 서비스 개발하기
커스텀 빈을 이용하여 GlassFish 관리 시스템 확장하기

EJB 3.0을 이용하여 웹 서비스 개발하기
 

Java EE, JSR 109를 위한 웹 서비스 명세에서는 웹 서비스를 구현하는 두 가지 방법을 정의한다. 그 중 한 가지 방법은 자바 클래스 프로그래밍 모델에 기초한 것으로, 웹 서비스가 웹 컨테이너에서 실행되는 자바 클래스에 의해 구현된다. 또 다른 방법은 EJB(Enterprise JavaBeans) 프로그래밍 모델에 기초한 것으로, 웹 서비스가 EJB 컨테이너에서 실행되는 stateless 세션 빈으로 구현된다. 이전의 테크 팁 JAX-WS를 이용한 웹 서비스 개발에서는 자바 클래스 프로그래밍 모델과 JAX-WS(Java API for XML Web Services) 2.0, JSR 224를 이용하여 웹 서비스를 개발하는 방법에 대해 알아보았다. 이번 팁에서는 JAX-WS와 EJB 프로그래밍 모델을 이용하여 웹 서비스를 개발하는 방법을 배우게 될 것인데, 특히 EJB 3.0 stateless 세션 빈을 이용하여 Calculator 서비스(이전 테크 팁에서 다룬 것과 동일한 Calculator 서비스)를 구현하는 방법에 대해 자세히 살펴보도록 하자.

본 팁에는 예제 패키지가 포함되어 있고, 이 예제에는 EJB 3.0 기반의 Calculator 서비스에 액세스하는 독립형 자바 클라이언트가 설명되어 있다. 또한 여기에는 GlassFish라고 불리는 Java EE 5의 오픈 소스 레퍼런스 구현이 사용되었다. GlassFish는 GlassFish 커뮤니티 다운로드 페이지에서 다운로드할 수 있다.

EJB 3.0 Stateless 세션 빈 클래스 작성하기

우선 서비스를 위한 stateless 세션 빈을 작성하는 것부터 시작해보자. Java EE 5 플랫폼에서 크게 개선된 내용 중 하나로 대폭 단순해진 EJB 프로그래밍 모델을 들 수 있는데, 이는 Enterprise JavaBeans 3.0 Specification, JSR-220에 정의되어 있다. 단순화된 기능 중 한 가지를 예로 들면, 빈 구현 클래스가 더 이상 javax.ejb.SessionBean 또는 javax.ejb.EntityBean 인터페이스를 구현하도록 요구하지 않는다는 것이다. 단순히 클래스에 주석을 달기만 하면 클래스를 세션 빈이나 엔티티 빈으로 선언할 수 있는데, 실례로 클래스에 @Stateless 주석을 달면 클래스를 stateless 세션 빈으로 선언할 수 있다.

EJB 3.0은 다음과 같이 빈 구현 클래스를 위한 추가 규칙을 명시한다.

  • 클래스는 반드시 public으로 선언되어야 하며 어떤 인자도 받지 않는 기본값 생성자(constructor)를 가져야 한다.

  • 클래스는 절대로 final 또는 abstract여서는 안 되며 반드시 톱 레벨 클래스여야 한다.

  • 클래스는 finalize() 메소드를 정의해서는 안 된다.

EJB 3.0에서 단순화된 또 한 가지 내용은 세션 빈을 위한 컴포넌트 인터페이스 또는 홈 인터페이스가 더 이상 요구되지 않는다는 점이다. 세션 빈이 필요로 하는 한 가지 인터페이스는 빈의 비즈니스 메소드를 정의하는 비즈니스 인터페이스이다. 비즈니스 인터페이스의 비즈니스 메소드는 컴포넌트 인터페이스의 비즈니스 메소드와 달리 java.remote.RemoteException을 throw할 필요가 없지만 비즈니스 메소드는 임의의 애플리케이션 예외와 관련하여 throws 절을 정의할 수 있다. 또한 EJB 3.0 비즈니스 메소드는 staticfinal이 될 수 없다.

이러한 단순화와 규칙을 전제로 하여, EJB 3.0 프로그래밍 모델을 따르는 Calculator 클래스의 stateless 세션 빈에 대해 알아보자(Calculator 클래스의 소스 코드는 설치된 예제 패키지의 엔드포인트 디렉토리에 들어 있음).

package endpoint; import javax.ejb.Stateless; @Stateless public class Calculator { public Calculator() {} public int add(int i, int j) { int k = i +j ; System.out.println(i + "+" + j +" = " + k); return k; } }

EJB 3.0 빈은 javax.ejb.SessionBean 인터페이스를 구현할 필요가 없기 때문에 더 이상 ejbActivateejbPassivate와 같은 비구현 라이프사이클 메소드를 포함시킬 필요가 없다. 따라서 훨씬 단순하고 완벽한 클래스가 생성된다. EJB 3.0에 정의된 다양한 주석들은 컴포넌트를 위한 배치 기술자 작성의 필요성을 줄이거나 없애줌으로써 개발자 및 배치자의 부담을 상당히 덜어준다.

EJB 3.0 빈을 웹 서비스로 표시하기

빈을 웹 서비스로 만들려면 간단히 클래스에 @WebService 주석을 달면 된다. 이것은 javax.jws.WebService 패키지에 정의된 주석 타입으로, Java Platform, JSR 181을 위한 웹 서비스 메타데이터에 지정되어 있다. 다음은 웹 서비스로 표시된 Calculator 클래스 코드이다.

package endpoint; import javax.ejb.Stateless; import javax.jws.WebService; @Stateless @WebService public class Calculator { public Calculator() {} public int add(int i, int j) { int k = i +j ; System.out.println(i + "+" + j +" = " + k); return k; } }

자바 클래스를 @WebService 주석으로 표시하면 서비스 구현 클래스가 된다. 단, 서비스 엔드포인트 인터페이스를 구현할 필요가 없다는 점에 유의할 것. JSR 109에 따르면, javax.jws.WebService 주석이 첨부된 서비스 구현 빈을 제공하기만 하면 된다. 그런 다음 배치 툴을 이용해서 자바 WSDL 매핑을 위한 JAX-WS 규칙을 적용함으로써 서비스 엔드포인트 인터페이스는 물론, WSDL 문서까지 생성할 수 있다.

웹 서비스 패키지하기

EJB 프로그래밍 모델에 기초한 웹 서비스는 JAR 파일로 패키지되어야 하는데, 즉 @WebService 주석을 이용하여 서비스 구현 빈 클래스(독립 클래스가 있을 경우에는 이와 함께)와 서비스 엔드포인트 인터페이스 클래스(명시적으로 제공될 경우)를 패키지하기만 하면 된다. 이 외에도, @Stateless 주석은 ejb-jar.xml을 패키지해야 하는 수고를 덜어주기도 한다. 이와 대조적으로, EJB 2.0이나 이전 버전에 기초한 웹 서비스를 JAX-RPC 방식으로 패키지하는 경우에는 사용자가 서비스 엔드포인트 인터페이스 클래스, 서비스 구현 빈 클래스(및 독립 클래스), 생성된 portable artifacts, JAX-RPC 매핑 파일, 웹 서비스 배치 기술자(webservices.xmlejb-jar.xml) 등을 제공해야만 했다.

JSR 224, JSR 109, JSR 181 및 JSR 220의 경우, 애플리케이션 서버 배치 툴을 이용하여 웹 서비스 배치를 위한 배치 기술자(사용자가 명시적으로 제공하지 않은 경우)를 비롯하여 필요한 모든 artifacts를 생성할 수 있다. EJB JAR 파일에 번들로 포함된 이 artifacts는 EJB 컨테이너에 배치된다. 배치자는 전술한 artifacts를 명시적으로 제공하고 배치 시 이를 EJB 모듈에 패키지함으로써 @WebService@Stateless 주석에 의해 지정된 값들을 오버라이트할 수 있다. 본 팁의 경우에는 다음 파일들이 배치될 EJB 모듈에 패키지되어 있다.

endpoint/Calculator.class endpoint/jaxws/Add.class endpoint/jaxws/AddResponse.class

나머지 배치 artifacts는 애플리케이션 서버(이 경우 GlassFish)에 의해 생성된다.

클라이언트 작성하기

웹 서비스를 배치한 후에는 클라이언트 프로그램으로 액세스가 가능한데, 클라이언트는 @WebServiceRef 주석을 이용하여 EJB 3.0 기반의 웹 서비스에 레퍼런스를 선언한다. @WebServiceRef 주석은 javax.xml.ws 패키지에 포함되어 있으며, Java Platform, JSR 181을 위한 JAX-WS 2.0 웹 서비스 메타데이터에 지정되어 있다. 본 팁에 사용된 클라이언트 프로그램 JAXWSClient의 소스 코드를 살펴보면(JAXWSClient의 소스 코드는 설치된 예제 패키지의 client 디렉토리에 들어 있음), 다음과 같은 내용을 확인할 수 있다.

@WebServiceRef(wsdlLocation= "http://localhost:8080/CalculatorService/Calculator?WSDL") static endpoint.CalculatorService service;

@WebServiceRef 내의 wsdlLocation 파라미터의 값은 일종의 URL로, 레퍼런스된 서비스의 WSDL 파일의 위치를 나타낸다. (@WebServiceRef 주석은 옵션으로 제공되는 추가 속성을 지원하며, 이 옵션 속성은 JAX-WS 2.0 스펙의 섹션 7.9에 지정되어 있다.) service로 명명된 정적 변수는 애플리케이션 클라이언트 컨테이너에 의해 인젝트된다.

JAXWSClient의 소스 코드를 좀더 살펴보면 다음과 같은 내용을 확인할 수 있다.

endpoint.Calculator port = service.getCalculatorPort();

service 오브젝트는 웹 서비스의 Calculator 포트에 액세스하기 위한 getCalculatorPort 메소드를 제공한다. 단, endpoint.CalculatorServiceendpoint.Calculator 모두 wsimport 유틸리티를 이용하여 생성되는 portable artifacts라는 점에 유의할 것. wsimport 유틸리티는 JAX-WS artifacts 생성에 사용되는데, 예제 프로그램 실행 시 클라이언트 구축을 위해 호출된다.

포트를 획득한 후에는 오브젝트 상에서 자바 메소드를 호출하는 것처럼 포트 상에서 비즈니스 메소드를 호출할 수 있다. 예를 들어, JAXWSClient의 다음 행은 Calculator의 add 메소드를 호출한다.

int ret = port.add(i, 10);

예제 코드 실행하기

본 테크팁에는 예제 패키지가 포함되어 있으며, 예제 패키지에서는 테크팁에서 다룬 기법을 예시한다. 예제를 설치하고 실행하려면 다음의 작업 절차를 따르도록 한다.

  1. GlassFish를 아직 구하지 못했다면 GlassFish 커뮤니티 다운로드 페이지에서 다운로드한다.

  2. 그런 다음 아래의 환경 변수를 설정한다.

    GLASSFISH_HOME: GlassFish의 설치 장소(가령 C:\Sun\AppServer)을 표시해야 한다.

    ANT_HOME: ant의 설치 장소를 표시해야 한다. ant는 다운로드한 GlassFish 번들에 포함되어 있다. (Windows에서는 lib\ant 서브디렉토리에 위치함)

    JAVA_HOME: 사용자 시스템에서의 JDK 5.0 위치를 표시해야 한다.

    아울러, ant 위치를 각자의 PATH 환경 변수에 추가한다.

  3. 해당 테크팁의 예제 패키지를 다운로드하여 압축을 푼다. 이 때, 새로 압축이 풀린 디렉토리는 <sample_install_dir>/ttmar2006ejb-ws로 표시되어야 하는데, 여기서 <sample_install_dir>은 예제 패키지가 설치된 디렉토리이다. 예를 들어, Windows의 C:\에 압축을 해제했다면 새로 생성된 디렉토리는 C:\ttmar2006ejb-ws가 되어야 한다. ttmar2006ejb-ws 아래의 ejb-techtip 디렉토리에는 예제를 위한 소스 파일과 기타 지원 파일이 포함되어 있다.

  4. ejb-techtip 디렉토리로 이동하여 build.properties 파일을 적절히 편집해야 하는데, 예를 들어 admin 호스트가 원격인 경우에는 admin.host의 기본값(localhost)을 해당 원격 호스트로 변경하면 된다.

  5. 다음 명령어를 입력하여 GlassFish를 시작한다.

    <GF_install_dir>/bin/asadmin start-domain domain1

    이 때, <GF_install_dir>은 GlassFish가 설치된 디렉토리를 나타낸다.

  6. ejb-techtip 디렉토리에서 다음 명령어를 실행한다.

    ant build

    빌드 디렉토리가 생성되고 클래스가 컴파일된 다음, 컴파일된 클래스가 빌드 디렉토리로 들어간다. 또한 아카이브 디렉토리와 JAR 파일이 생성되고, JAR 파일이 아카이브 디렉토리로 들어간다.

    ant deploy

    이 명령어는 JAR 파일을 GlassFish 상에 배치한다.

    ant build-client

    이 명령어는 portable artifacts를 생성하고 클라이언트 소스 코드를 컴파일한다.

    ant run

    이 명령어는 애플리케이션 클라이언트를 실행하고 Calculator 서비스에서 add 연산을 10회 호출하여 0에서 9까지의 숫자에 10을 추가한다. 이 때, 다음과 같은 내용이 표시되어야 한다.

    실행:

    [echo] Executing appclient with client class as client.JAXWSClient [exec] Retrieving port from the service endpoint.CalculatorService@159780d [exec] Invoking add operation on the calculator port [exec] Adding : 0 + 10 = 10 [exec] Adding : 1 + 10 = 11 [exec] Adding : 2 + 10 = 12 [exec] Adding : 3 + 10 = 13 [exec] Adding : 4 + 10 = 14 [exec] Adding : 5 + 10 = 15 [exec] Adding : 6 + 10 = 16 [exec] Adding : 7 + 10 = 17 [exec] Adding : 8 + 10 = 18 [exec] Adding : 9 + 10 = 19
  7. GlassFish에서 EJB 모듈을 배치 해제하려면 다음 명령어를 실행한다.

    ant undeploy

저자 소개

Manisha Umbarje는 Sun Java System Application Server의 제품 엔지니어링 그룹 소속임.

맨위로

커스텀 빈을 이용하여 GlassFish 관리 시스템 확장하기
 

J2SE(Java 2 Platform, Standard Edition) 5.0에서 크게 개선된 부분으로 모니터링과 관리 기능을 들 수 있으며, J2SE 5.0에 도입된 주요 특징에는 instrumented JVM(Java Virtual Machine)과 JVM을 모니터하고 감시하기 위한 java.lang.management API가 포함된다. 또한 API는 메모리 이용률, 스레드 상태, 클래스 로딩 활동, 가비지 컬렉션 전략 등을 비롯하여 다양한 JVM 관련 정보에 대한 로컬 및 원격 액세스를 가능하게 해준다. 이와 같이 강화된 통합 관리 및 모니터링 지원 기능은 J2SE 5.0 기반의 어떠한 관리 애플리케이션에서도 이용이 가능하다.

GlassFish와 MBeans

이런 기능을 활용하는 툴 중 하나가 바로 Java EE 5의 오픈 소스 애플리케이션 서버 구현인 GlassFish인데, GlassFish 애플리케이션 서버는 자체의 관리 인프라 내에서 J2SE 5.0 기반의 강화된 모니터링 및 관리 기능을 모두 활용한다. 또한 여기에는 플랫폼 MBean 서버를 이용하여 애플리케이션 서버 MBeans를 등록하는 기능도 포함되어 있다. Management Beans(일명 "MBeans")는 JMX(Java Management Extensions) 스펙에 준한 디자인 패턴을 따르는 자바 오브젝트로, 리소스를 공급하는 데 이용된다. 본 팁은 사용자가 JMX에 대한 기본 지식을 이미 가지고 있는 것으로 가정한다. JMX에 관한 개요를 보려면 테크팁 JMX 기술 이해하기를 참조할 것.

J2SE 5.0에 함께 도입된 플랫폼 MBean 서버는 JVM에 내장된 JMX 기반 에이전트로, 관리 애플리케이션이 MBeans에 액세스할 수 있게 해준다. 이 에이전트는 JVM에서 실행되는 모든 매니지드 컴포넌트에 의해 공유될 수 있다. GlassFish는 플랫폼 MBean 서버를 통해 시스템과 애플리케이션별로 MBeans에 대한 종합적이고 통합된 모니터링 및 관리 뷰를 제공할 수 있으며, J2SE 5.0 이전에 요구되었던 것처럼 자체 에이전트의 인스턴스를 생성하는 대신 이 내장 에이전트를 이용할 수 있다. 애플리케이션 서버 MBeans는 플랫폼 MBean 서버 리포지토리에 등록되기 때문에, 자바 모니터링 및 관리 콘솔(JConsole)과 같은 툴은 애플리케이션 서버 MBeans에 의해 제공되는 모든 리소스에 관한 정보를 디스플레이할 수 있다.

GlassFish와 커스텀 MBeans

사용자는 자체 커스텀 MBeans를 생성하여 등록함으로써 GlassFish의 관리 기능을 확장할 수 있다. 커스텀 MBeans는 모니터링 및 관리 기능의 동적 인젝션을 가능케 해줄 뿐 아니라 instrumented 커스텀 MBean 속성을 지속시켜 줌으로써 애플리케이션 서버를 재시작해야 할 경우에라도 활성 상태를 그대로 유지할 수 있게 한다. 커스텀 MBeans의 유용성을 엿볼 수 있는 한 가지 예가 바로 GlassFish의 자체 관리 프레임워크인데, 이는 규칙 기반의 이벤트 관리 시스템으로, 사용자는 커스텀 MBeans로 구현되는 커스텀 액션을 시스템 관리 규칙에 추가할 수 있다.

예제: 모니터링 및 관리 기능 확장하기

본 팁에서는 커스텀 MBeans를 이용하여 GlassFish의 모니터링 및 관리 기능을 확장하는 예제를 제시하고, 이 예제를 통해 웹 서비스로부터 정기적으로 획득하는 데이터를 처리하는 Java EE 애플리케이션을 확장해 보도록 한다. 또한 관리 확장을 위해 모니터 대상 이벤트, 그리고 그 이벤트에 대한 응답으로 애플리케이션이 취할 액션을 추가하도록 한다.

이벤트는 이메일 주소 목록을 업데이트하는 것이고, 액션은 업데이트된 이메일 주소 목록으로 이메일을 전송하는 것이다. 이 시나리오에서, 웹 서비스의 클라이언트이기도 한 커스텀 MBean은 웹 서비스를 폴링한 후에 이메일 주소 목록을 업데이트한다. 하지만, 이 예제에서는 단순화를 위해서 이메일 주소의 문자열이 커스텀 MBean의 속성으로 되어 있다. 사용자는 JConsole을 이용하여 직접 목록을 수정하고, 속성이 수정되면 리스닝 커스텀 MBean에 통지가 이루어진다. 그런 다음 리스닝 커스텀 MBean이 업데이트된 이메일 주소 목록(즉, 업데이트된 속성의 값)에 이메일을 전송하여 이벤트를 처리한다.

본 테크팁에는 예제 아카이브가 포함되어 있으며, 여기에는 예제를 위한 소스 코드, 그리고 컴파일된 클래스를 비롯하여 예제에서 사용되는 클래스를 위한 javadoc이 함께 포함되어 있다.

1단계: 셋업

초기 설정 작업부터 시작해보자. GlassFish를 아직 구하지 못했다면 GlassFish 다운로드 페이지에서 다운로드한 다음 아래의 환경변수를 설정한다.

  • GLASSFISH_HOME. GlassFish의 설치 장소(가령 C:\Sun\AppServer)을 표시해야 한다.

  • JAVA_HOME. 사용자 시스템에서의 JDK 5.0 위치를 표시해야 한다.

2단계: GlassFish 시작하기

다음 명령어를 입력하여 GlassFish를 시작한다.

<GF_install_dir>/bin/asadmin start-domain domain1

이 때, <GF_install_dir>은 GlassFish가 설치된 디렉토리를 나타낸다.

3단계: 예제 아키이브 다운로드하기

해당 팁의 예제 아카이브를 다운로드하여 압축을 푼다.

jar xvf ttmar2006custmbean.jar

압축을 풀고 나면 META-INF, techtip 및 javadoc 디렉토리가 표시되어야 한다. techtip 디렉토리에는 예제용 소스 코드, 그리고 컴파일된 클래스가 포함되어 있다.

4단계: 커스텀 MBeans 생성하기

본 예제에서는 2개의 커스텀 MBeans--이벤트 소스용과 이벤트 청취용(이벤트 "싱크")--를 사용한다. 이 2개의 MBeans를 위한 소스 코드는 techtip/src 디렉토리에 들어 있다.

커스텀 MBean은 MBean 인터페이스와 MBean 구현 클래스를 필요로 하는데, 이벤트 소스용 커스텀 MBean 인터페이스는 EventSourceCustomMBean이며, 그 구현 클래스는 EventSourceCustom이다. 그리고 이벤트 싱크를 위한 커스텀 MBean 인터페이스는 EventSinkCustomMBean이며, 그 구현 클래스는 EventSinkCustom이다.

커스텀 MBeans를 위한 소스 파일을 생성한 후에는 파일들을 com.example.mbeans 패키지 구조에 컴파일할 수 있다(이는 옵션사항이며, 컴파일된 클래스는 예제 아카이브에 포함되어 있음). 파일을 컴파일하려면 다음을 입력한다.

javac -d . EventSourceCustom.java EventSourceCustomMBean.java javac -d . EventSinkCustom.java EventSinkCustomMBean.java

EventSinkCustom 클래스를 컴파일할 때는 각 사용자의 클래스패스에 mail.jaractivation.jar 파일이 들어 있는지 확인해야 한다.

5단계: 커스텀 MBean 클래스 복사하기

com/example/mbeans 디렉토리와 그 내용을 MBean 클래스로더 디렉토리에 복사한다. MBean 클래스로더 디렉토리는 <domain>/applications/mbeans이고, 이 때 <domain>은 애플리케이션이 배치된 도메인을 나타낸다. 본 예제에서는 애플리케이션이 기본값 GlassFish 도메인인 domain1에 배치된 것으로 가정한다. 따라서 com/example/mbeans 디렉토리와 그 내용을 <GF_install_dir>/glassfish/domains/domain1/applications/mbeans에 복사한다. 그러면 /com/example/mbeans 디렉토리가 생성되고 커스텀 MBean 클래스와 인터페이스가 <GF_install_dir>/glassfish/domains/domain1/applications/mbeans에 복사된다.

6단계: 이벤트 소스 커스텀 MBean 배치하기

다음과 같이 이벤트 소스 커스텀 MBean, EventSourceCustom을 배치한다(명령어는 한 행에 입력해야 한다).

<GF_install_dir>/bin/asadmin create-mbean --user admin --passwordfile password.txt --port 4848 com.example.mbeans.EventSourceCustom

플랫폼 MBean 서버에 EventSourceCustom이 등록된다.

다음 명령어를 이용하여 커스텀 MBean이 배치되었는지 확인할 수 있다(명령어는 한 행에 입력해야 한다).

<GF_install_dir>/bin/asadmin list-mbeans --user admin --passwordfile password.txt

다음과 같은 내용이 표시되어야 한다.

com.example.mbeans.EventSourceCustom user:impl-class-name=com.example.mbeans.EventSourceCustom, name=com.example.mbeans.EventSourceCustom Enabled Command list-mbeans executed successfully.

7단계: 이벤트 싱크 커스텀 MBean 배치하기

다음과 같이 이벤트 싱크 커스텀 MBean, EventSinkCustom을 배치한다(명령어는 한 행에 입력해야 한다).

<GF_install_dir>/bin/asadmin create-mbean --user admin --passwordfile password.txt --port 4848 --objectname "user:impl-class-name=com.example.mbeans.EventSinkCustom, name= custom-event-sink" --attributesHostName=<yourmailserver>:Id=<yourId>: Password=<yourpw> com.example.mbeans.EventSinkCustom

단, 명령어의 HostName, Id, Password 등의 파라미터에 유의할 것. 메일 서버에 접속하려면 이 파라미터들이 지정되어야 한다. 파라미터는 커스텀 MBean의 속성이다. <yourmailserver>는 각자의 메일 서버 이름으로, <yourId>는 각자의 ID로, <yourpw>는 각자의 비밀번호로 대체한다.

앞에서 언급한 것처럼, instrumented 커스텀 MBean 속성을 지속시켜 줌으로써 애플리케이션 서버를 재시작해야 할 경우에라도 활성 상태를 그대로 유지할 수 있다. 따라서, 명령어의 파라미터를 지정하면 커스텀 MBean의 런타임 파라미터가 구성된다. 하지만 MBean이 생성된 후에는 서버가 재시작하더라도 파라미터는 기존의 상태를 지속하게 된다.

이번에도 다음 명령어를 이용하여 커스텀 MBean이 배치되었는지 확인할 수 있다(명령어는 한 행에 입력해야 한다).

<GF_install_dir>/bin/asadmin list-mbeans --user admin --passwordfile password.txt

다음과 같은 내용이 표시되어야 한다.

com.example.mbeans.EventSourceCustom user:impl-class-name=com.example.mbeans.EventSourceCustom, name=com.example.mbeans.EventSourceCustom Enabled com.example.mbeans.EventSinkCustom user:impl-class-name=com.example.mbeans.Event SinkCustom, name=custom-event-sink Enabled Command list-mbeans executed successfully.

8단계: 이메일 목록 업데이트하기

본 예제에서는 JConsole을 이용하여 이메일 주소의 문자열이 포함된 커스텀 MBean의 속성을 업데이트한다.

다음 명령어를 입력하여 JConsole을 시작한다.

<JAVA_HOME>/bin/jconsole

JConsole를 시작하면 Local/Remote/Advanced JMX 접속 탭이 있는 접속 대화상자가 표시되어야 한다. Remote 탭을 클릭하고 해당 호스트(localhost), 포트(GlassFish의 JMX 접속 서버는 포트 8686에서 청취), 사용자 이름(admin), 비밀번호(adminadmin) 등의 값을 입력한다.

Remote 탭의 디스플레이에서 Connect 버튼을 클릭하면 RMI 커넥터를 통해 플랫폼 MBean 서버로의 접속이 이루어진다. 이어지는 Connection 창에서 MBeans 탭을 클릭하고, 창의 왼쪽 구획에 위치한 MBean 트리에서 사용자 브랜치를 확장한다. com.example.mbeans.EventSourceCustom 아래에 서버 엔트리가 표시될 때까지 사용자 브랜치의 하위 브랜치들을 계속 확장하고, 서버 엔트리를 클릭한다. 이제 창의 오른쪽 패널에 있는 Infor 탭에 MBean에 관한 정보가 표시되어야 한다.

창의 오른쪽 패널에 있는 Attributes 탭을 클릭한 다음 EmailRecipientsString 속성을 위한 값 필드에 각자의 이메일 주소를 입력한다.

EmailRecipientsString 속성을 변경하면 이벤트 소스 커스텀 MBean이 통지를 발송하는데, 이 때 AttributeChangeNotification 클래스에 의해 정의되는 통지를 청취하고 이 속성의 새 값을 가져온다. 다음은 이 작업과 관련한 EventSinkCustom 내의 코드이다.

AttributeChangeNotification attrnotif = (AttributeChangeNotification) notif; String newEmailIdStr = (String)attrnotif.getNewValue();

이어서 이벤트 싱크 커스텀 MBean이 업데이트된 주소로 이메일을 전송한다.

9단계: 이메일 확인하기

리스닝 커스텀 MBean이 각자의 주소로 이메일을 전송했는지 확인하기 위해 우선 이메일을 살펴본다. 다음과 같은 메시지가 수신되어야 한다.

This message has been generated and sent as part of the Glassfish Techtip sample example execution

더 자세한 내용은 다음 사이트를 참조할 것.

저자 소개

Nandini Ektare는 썬 마이크로시스템즈의 썬 자바 시스템 애플리케이션 서버 개발 그룹 소속으로, JMX와 Java EE이 그녀의 주요 관심 분야이다.

Kedar Mhaswade는 썬 마이크로시스템즈의 스태프 엔지니어로, JSR 003: Java Management Extensions 전문가 그룹의 일원이다. 그의 주요 관심 분야로는 오픈소스 소프트웨어, JMX, Java EE 등이 있다.

맨위로

본 메일은 수신을 동의한 회원님에게만 발송됩니다.
본 메일의 수신을 거부하거나 수신주소를 변경하려면 SKDN@Sun.com으로 문의 주시기 바랍니다.

SKDN(Sun Korea Developers Network)에서 J2EE/J2SE 테크팁 등 다양한 아티클들을 참고하세요.

Copyright 2003-2006 Sun Korea, Ltd. All rights reserved.


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

[java mail] access to default session denied 에러발생시...  (1) 2007.07.05
http://xfire.codehaus.org/  (0) 2006.11.01
[펌]JMS(Java Messaging System)  (0) 2006.02.06
[링크] Oracle sample-code  (0) 2005.08.19
[링크]SmartClient  (0) 2005.08.10
Posted by tornado
|
JMS는 Java Messaging System의 약자로 Java에서 Messaging System을 사용하기 위한 API들의 정의 이다.

Messaging System


먼저 Messaging System에 대해서 알아보도록 하자. Messagign System이란, Application과 Application이 서로 통신을 하도록 지원해주는 시스템을 이야기 한다.

예를 들어서 설명해보자. 각 지점에 설치된 매출 관리 시스템 A라는 AP(※ Application의 약자, 이하 AP)와 본점에 설치된 B라는 AP가 있다고 하자, A라는 AP는 매출이 발생할때마다, 그 내용을 매장에 있는 PC에도 저장하지만, 그 내용을 본사의 Unix Machine에 전송해서 추후에 총합하도록 하게 한다고 하자.

일반적으로 Messaging System이 만들어지기 전에는 이런 업무를 A와 B 시스템간에 Socket을 직접 연결해서 Packet을 정의하고 그 Packet에 따라서 통신을 했다. 이 통신을 위해서, Packet에 대한 flow control이나, 네트워크에 문제가 발생했을때의 예외처리등을 다 직접 프로그래밍을 해야 했다. 그러나 Messaging System은 이런 모든 AP간의 통신에 대한 여러 기능들을 제공하여 통신에 대한 부분을 간결화 시켜준다

Messaging System에는 이외에도, 하나의 Publisher(방송者)가 여러 Subscriber(구독者) 에게 메세지를 전송하는 모델이라던지, P2P모델 그리고, 메세지가 중간에 유실되지 않게 하는 Reliable Messaging, 비동기방식으로 메세지를 전달하는 방법, 분산 트렌젝션, 클러스터링등을 지원한다. 이 내용에 대해서는 뒤에서 좀 더 자세하게 알아보도록 하자.


What is JMS?


그렇다면 JMS는 무엇인가? JMS는 이런 메세징 시스템을 Java에서 사용하기 위한 표준 API이다. 우리가 DBMS를 사용하면서 JDBC를 사용하는것처럼, JMS는 Messaging 시스템을 사용하기 위한 API의 집합이다.


<그림 1. jms 시스템 개념도>


그림을 살펴보자, Java Application은 표준화된 JMS API를 이용해서 Messaging System을 사용하게 된다. 이 JMS API는 각각 Messaing System Vendor에서 제공되는 Provider Code를 이용해서 Implementation되어 있기 때문에, Java Application 개발자는 JMS라는 표준 API만 사용하면 대부분의 Messanging System을 사용할 수 있다.

※ Notice !! - JMS라는 표준이 있기는 하지만 각각의 Messaging System의 특성에 따라 달라질 수 있다. 작동구조나 성능 역시 각각의 Messaging System 마다 차이가 있기 때문에. 이를 충분히 고려해서 사용해야한다.


<그림 2. jms 시스템 개념도>


Messaging System의 경우 JMS API뿐 아니라 C와 같은 Non-Java AP를 위한 Lib Code를 제공하는 경우가 있는 데 이런 경우에는 Java Application이 아닌 다른 언어로 개발된 Application과도 호환이 되기 때문에, JMS기반의 Messaging 시스템은 Mainframe이나 다른 Application들과 Java Application을 연동하는데 많이 사용이 되며, 근래에 많이 출시되는 EAI솔루션도 JMS를 이용하는 경우가 많다.

우리가 흔히 이야기하는 JMS Product는 이 Messaging System과 JMS API 가 함께 제공되는 Product를 이야기 한다. (Sonic MQ,WebLogic JMS,Oracle Advanced Queing.. etc.)


Feature of JMS


지금까지 JMS에 대한 간단한 개념을 알아봤다. 그럼 JMS API에는 어떤 특징이 있는지 하나씩 살펴 보기로 하자.
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을 이용하여, 별도의 보장 없이 메세지를 빨리 전송하는데만 초점을 맞출 수 있다.


Publish/Subscribe model


JMS Messaging System은 sender와 receiver를 지원하는 모델에서도 다소 차이가 있다. Interface centric model의 경우(IIOP,RMI. Etc)의 경우 하나의 sender는 하나의 connect된 recevier에만 message를 보내는 1:1 (peer-to-peer) model이다. JMS는 이런 peer-to-peer 모델이외에 1:N (publish/subscriber model)과 , 특정 메세지그룹의 메세지만을 특정 그룹이 받을 수 있게 할 수 도 있다.


Queue/Bus


JMS Messaging 시스템에서 sender가 메세지를 보내는 destination이 되고, reciever가 메세지를 읽어오게 되는 부분을 우리는 Queue 또는 Bus라고 이야기한다.


<그림 3. JMS Queue의 개념>


그림 3을 보면 좀더 쉽게 이해를 할 수 있다. 포탈 쇼핑몰에 입점한 여러개의 쇼핑몰이 있다고 하자. 포탈쇼핑몰에서는 주문 내용을 메세지로 만들어서 보내고, 이 메세지를 컴퓨터에 대한 주문은 QueueA로, 전자제품에 대한 주문은 Queue B로 보낸다고 한다. 컴퓨터 판매 쇼핑몰은 QueueA에서만 주문 정보를 받고, 전자제품 쇼핑몰을 QueueB에서 주문을 받는다. Queue는 이처럼 Sender와 Receiver간에 Message를 주고 받는 채널의 역할을 한다.

여기서 만약 포탈 쇼핑몰이 하나 더 늘어 났다고 하자. 그러면 주문 연동은 어떻게 할것인가? 답은 간단하다. 새로운 포탈 쇼핑몰 B가 컴퓨터 판매 쇼핑몰에 주문을 넘기기 위해서는 Queue A에 주문 Message를 보내기만하면 된다 < 그림 3-1. 포탈 쇼핑몰이 늘어난 경우 >


<그림 3-1. 포탈 쇼핑몰이 늘어난 경우>


마찬 가지 방법으로, 포탈 쇼핑몰 A가 컴퓨터 판매에 대한 주문을 컴퓨터 판매 쇼핑몰이 아니라, 전자 제품 판매 쇼핑몰에 하고자 할때는 컴퓨터 판매에 대한 주문을 Queue B에만 넣어주면 된다.

이처럼 JMS Messaging 시스템에는 Queue의 개념을 이용하면, Message의 경로 배정( Routing)을 매우 유연적으로할 수 있으며, 이런 유연성은 업무 흐름 (Work flow)를 구현하는데 큰 강점으로 작용한다.

그럼 이렇게 기능이 좋은 JMS를 통신에는 다 사용하면 될것인가? 당연히 대답은 NO다. 이메일을 위해서는 이미 SNMP라는 좋은 프로토콜이 개발되어 있고, 이를 이용하기 위한 JavaMail API가 있다. Audio나 Video Streaming 같은 경우에는 Real Time이 중요하다, JMS는 오히려 Async 메세징에 더 유리하다고 볼 수 있다. JMS Messaging 시스템의 특성에 대해서 제대로 파악하고, 적재 적소에 사용한다면 좀더 뛰어나고 안정된 상호 운영성을 확보할 수 있을 것이다.

지금까지 JMS 시스템의 대략적인 구조에 대해서 살펴보았다.. 처음 접하는 사람은 쉬운 개념이 아니었을지도 모르지만, JMS 시스템이 대략 어떤 개념인지만 이해한다면 충분히 성공한것이다. 좀 더 구체적인 내용과 구현방법은 JMS 시스템에 대한 서적을 참고하기 바란다.

※ 참고 자료 Enterprise JMS Programmin / Shaun Terry - M Books
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
|

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
|
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 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
|
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
|
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
|
Posted by tornado
|