서비스 지향 아키텍처(SOA) 와 서비스 지향 프로그래밍은 비즈니스 로직을 모듈식 서비스로 캡슐화 하는 소프트웨어 엔지니어링 스타일을 칭하는 용어이다. 이 서비스는, 서비스 공급자와 서비스 소비자 간 제휴가 약한(loosely-coupled) 동적인 런타임 환경을 주 타겟으로 한다. 약결합 상태의 서비스들은 컴파일 시 제휴되지 않으므로 런타임 시 동적으로 연결할 수 있고, 개발자들은 필요에 따라 전개를 할 수 있다. 약결합(loose coupling)외에도 서비스 지향 환경의 몇 가지 특성을 요약하면 다음과 같다.
- 대단위(Coarse granularity): 서비스와 관련된 세분성이라 함은 서비스를 공개적으로 표현하는 기능과 관련이 있다. 소단위(Fine-grained) 서비스들은 특정 수준의 기능을 정의하는 공식 인터페이스를 말한다. 대단위 서비스(grained services)들은 해당 비즈니스 영역에 맞는 보다 일반적인 수준의 기능을 나타낸다.
- 위치 투명성(Location transparency): 서비스가 네트워크의 어느 곳에 있든지 클라이언트는 서비스에 액세스 할 수 있다.
- 프로토콜 독립(Protocol independence): 통신/네트워크 프로토콜과 관계 없이 클라이언트는 서비스에 액세스 할 수 있다.
이러한 개념들을 모두 적용하여 서비스를 구현하는 일은 어려운 일이 아닐 수 없다. POJO 프로그램이 이 태스크를 단순화 시킬 수 있다.
POJO는 특정한 외부 인터페이스나 서드-파티 API에 의존하지 않는 자바 클래스이다. 따라서 코드를 외부적 제휴 상태와 분리할 수 있다. 디커플링(decoupling)의 주요 이점들 중 하나가 소프트웨어 개발자들에게 주어지는 자유이다. 지속성(persistence), 트랜잭션 지원, 리모팅(remoting)같은 가외 작업들로부터 개발자는 해방된다. 많은 기술들이 컴포넌트/클래스 디커플링을 줄이고 POJO 프로그래밍을 활용하고 있다.
- 주석(Annotations)은 리모팅, 지속성, 프레임웍 지원 같은 기능이나 특성을 지원하기 위해 클래스나 클래스의 일부를 "꾸미는" 코드를 생성하기 위해 개발 툴이 사용하는 메타데이터이다.
- 의존성 삽입(Dependency injection)은 플러거블(pluggable) 컴포넌트를 구현하는 기술로서, 이것으로 객체 생성과 제휴가 컴포넌트가 아닌, 컨테이너나 어셈블러 컴포넌트에 의해 수행된다.
- 리플렉션(Reflection)은 주어진 클래스나 인터페이스에 대해 메소드, 필드, 컨스트럭터 같은 정보의 발견이다.
각각의 디커플링 기술은 장단점이 있다. 이 글에서는 리플렉션과 Geronimo의 GBean 의존성 삽입을 사용하는 POJO 프로그래밍을 사용하는 SOA 프레임웍에 대해 설명하겠다.
Geronimo는 Java Management Extensions (JMX)와 GBeans이라고 하는 관리 컴포넌트의 의존성 삽입 프레임웍을 사용하는 범용 커널에 구현된다. Geronimo의 가상의 모든 것들(어댑터, 애플리케이션, 컨테이너)은 GBean이거나, GBean에 기반을 두고 있다. GBean은 JMX와 JMX Managed Beans (MBeans)과 유사하며 인프라스트럭처도 공유하고 있다.
GBean은 Geronimo에서 관리되는 컴포넌트이다. JMX MBean과 유사점도 많고 관련도 많다. GBeanInfo라는 클래스에 따라 애트리뷰트와 연산을 노출한다. 이는 JMX의 MBeanInfo 클래스와 매우 비슷하다. Geronimo는 MX4J 라이브러리를 사용한다. (Resources)
GBean은 상태와 제휴 의존성을 관리하고 라이프 사이클 이벤트를 핸들한다. GBean은 다른 GBean의 상태에 관련 당사자로서 등록한다. GBean이 시작한 후에 GBean은 의존성 삽입을 통해 해당 GBean에 대한 레퍼런스를 받는다. GBean은 주어진 시간에 이러한 7 개의 라이프 사이클 상태 중 하나가 된다:
- Loaded
- Unloaded
- Starting
- Running
- Stopping
- Stopped
- Failed
Listing 1은 애트리뷰트(메시지)를 포함하고 있는 간단한 GBean이다.
Listing 1. 전형적인 Gbean
public class TestGBean
implements GBeanLifecycle
{
private static GBeanInfo GBEAN_INFO = null;
static
{
GBeanInfoBuilder infoFactory =
GBeanInfoBuilder.createStatic(TestGBean.class);
infoFactory.addAttribute("message", String.class, true);
infoFactory.addOperation("getMessage");
GBEAN_INFO = infoFactory.getBeanInfo();
}
private String message;
public String getMessage()
{
return message;
}
...
}
|
Listing 2의 코드로 GBean을 시작(활성화) 및 중지할 수 있다.
Listing 2. GBean 시작하기
ObjectName testGBeanOName =
ObjectName.newInstance("jeffhanson.test:ID=test");
GBeanData gBeanData =
new GBeanData(testGBeanOName, TestBean.GBEAN_INFO);
gBeanData.setAttribute("message", "Hello world");
geronimoKernel.loadGBean(gBeanData,
Thread.currentThread().
getContextClassLoader());
geronimoKernel.startGBean(testGBeanOName);
...
geronimoKernel.stopGBean(testGBeanOName);
geronimoKernel.unloadGBean(testGBeanOName);
|
Geronimo 커널을 통해 GBean을 사용할 수 있다.
A Geronimo 커널은 GBean의 프레임웍이다. 이 프레임웍을 사용하여 어떤 고급 시스템이라도 GBean 컨테이너와 GBean 컴포넌트로서 모델링 및 구축하여 상태, 관계, 이벤트 핸들링을 관리할 수 있다.
Geronimo 커널을 프로그래밍 방식으로 구현할 때 KernelFactory 클래스를 사용하면 간단하다. Listing 3은 TestGeronimo라는 새로운 Geronimo 커널을 만들고, 커널을 부팅하고, 부팅 시 로깅하며 서블릿 GBean을 로딩 및 시작하는 방법을 설명하고 있다.
Listing 3. Geronimo 커널 만들기
try
{
Kernel geronimoKernel =
BasicKernelFactory.newInstance().
createKernel("TestGeronimo");
geronimoKernel.boot();
log.debug("Geronimo BootTime: "
+ geronimoKernel.getBootTime());
// add the servlet GBean
ObjectName servletObjName =
new ObjectName("jeffhanson.test:ID=MyGBean");
GBeanData servletGBeanData = new GBeanData(servletObjName,
GBEAN_INFO);
ClassLoader classLoader = getClass().getClassLoader();
geronimoKernel.loadGBean(servletGBeanData, classLoader);
geronimoKernel.startGBean(servletObjName);
}
catch (Exception e)
{
log.error(e);
}
|
커널을 만들고 실행한 후에 POJO 서비스에 대해 메소드를 실행할 때 Geronimo 커널 서버와 이것의 리플렉션 기능을 사용하면 쉬워진다. (Listing 4)
Listing 4. 커널과 함께 등록된 서비스 호출하기
private static
Object invokePOJOService(ObjectName serviceObjName,
String operationName,
String[] params)
throws Exception
{
String[] paramTypes = null;
if (params != null && params.length > 0)
{
paramTypes = new String[params.length];
for (int i = 0; i < params.length; i++)
{
paramTypes[i] = params[i].getClass().getName();
}
}
Kernel geronimoKernel =
KernelManager.getInstance().getGeronimoKernel();
Object retVal =
geronimoKernel.invoke(serviceObjName,
operationName,
(Object[])params,
paramTypes);
return retVal;
}
|
POJO-for-SOA 프레임웍은 Geronimo 커널의 인스턴스를 사용하여 POJO를 GBean으로서 등록하여, 관련 클라이언트가 추가 인터페이스나 API 없이 이들을 호출 및 쿼리 할 수 있도록 한다. 이 프레임웍은 멀티 티어 엔터프라이즈 애플리케이션 환경에서 비즈니스 티어에 있다. service-locator 클래스는 이 커널과 인터랙팅 하여 POJO가 서비스로서 사용될 수 있도록 하는 책임을 맡고 있다. service-locator 클래스는 business-delegate 컴포넌트로 POJO를 리턴한다. 그림 1은 프레임웍 내의 컴포넌트들 간 관계도이다.
그림 1. POJO-for-SOA 프레임웍
이 프레임웍은 클라이언트에서 HTTP 요청을 받고 요청을 business-delegate 컴포넌트로 요청을 보내는 디스패처 컴포넌트를 통해 요청을 전달한다. business-delegate 컴포넌트는 service-locator를 사용하여 해당 요청에 대한 서비스를 찾는다. business-delegate 컴포넌트는 서비스를 호출하고 리턴 값을 모델 객체로 래핑한다. 해당 뷰 컴포넌트는 모델 객체를 처리하고 이것을 클라이언트에게 응답 포맷을 사용하여 보낸다. 그림 2는 시퀀스 다이어그램이다.
그림 2. 전형적인 HTTP 요청 및 서비스 호출 시퀀스
그림 3의 클래스 다이어그램은 프레임웍의 클래스들 간 관계도이다.
그림 3. 프레임웍 클래스들 간 관계
이 프레임웍은 엔터프라이즈 애플리케이션 시스템의 비즈니스 티어에 있다. 프레임웍은 HTTP 요청을 받고 내용을 프레임웍으로 보내는 하나의 서블릿을 노출한다. 다음 섹션에서는 간단한 전개 과정을 설명하겠다.
.war 파일에 프레임웍용 클래스와 엔터프라이즈 애플리케이션을 패키징하여 이를 geronimo_home/deploy 디렉토리에 둔다. 디렉토리가 없다면 만들기 바란다.
Geronimo는 시작 시 .war 파일을 자동으로 전개한다. 전개 디렉토리에 있는 애플리케이션들이 로딩되고, Geronimo는 변경 사항이 생길 경우 런타임 시 애플리케이션을 다시 로딩한다. 따라서 애플리케이션 디버깅이 매우 편리하다.
geronimo_home/bin 디렉토리에 있는 시작 스크립트(startup.bat 또는 startup.sh)를 사용하여 Geronimo 애플리케이션 서버를 시작한다. Geronimo 시작 스크립트를 호출하면 Geronimo 콘솔 윈도우가 보인다. 프레임웍과 애플리케이션을 전개한 후에 Geronimo의 콘솔 윈도우에는 Listing 5와 비슷한 라인들이 생기면서 웹 애플리케이션이 성공적으로 시작되었다는 것을 확인한다.
Listing 5. 웹 애플리케이션이 성공적으로 시작되었다!
0 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel - Starting boot 422 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState - GBeanInstanceState for: :role=Kernel State changed from stopped to starting 422 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState - GBeanInstanceState for: :role=Kernel State changed from starting to running 422 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel - Booted 640 [main] DEBUG com.jeffhanson.apptier.FrontController - Geronimo BootTime: Sat May 20 18:51:08 MDT 2006 656 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState - GBeanInstanceState for: jeffhanson.test:ID=FrontController State changed from stopped to starting 656 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState - GBeanInstanceState for: jeffhanson.test:ID=FrontController State changed from starting to running |
다음 URL을 웹 브라우저 윈도우에 입력하여 HelloWorld 서비스에 대해 setMessage 연산 수행한다:
http://<host>:<port>/<context>?Action=
HelloWorld&Operation=setMessage&Params=Hello+everybody!
프레임웍이 요청을 처리하면 콘솔 아웃풋은 다음과 같을 것이다. (Listing 6)
Listing 6.
setMessage 연산 프로세싱 결과 719 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator - Adding service [HelloWorld] to kernel... 719 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator - Loading GBean: jeffhanson.test:Name=HelloWorld,Type=GenericService 734 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState - GBeanInstanceState for: jeffhanson.test:Name=HelloWorld,Type=GenericService State changed from stopped to starting 734 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState - GBeanInstanceState for: jeffhanson.test:Name=HelloWorld,Type=GenericService State changed from starting to running |
다음 URL을 웹 브라우저 윈도우에 입력하고 HelloWorld 서비스에 sayHello 연산을 실행한다.
http://<host>:<port>/<context>?
Action=HelloWorld&Operation=sayHello
프레임웍이 요청을 처리할 때 콘솔 아웃풋은 다음과 같이 된다. (Listing 7)
Listing 7.
sayHello 처리 결과750 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator - serviceObjName: jeffhanson.test:Name=HelloWorld,Type=GenericService 750 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator - Service [HelloWorld] already in kernel 1156 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator - serviceObjName: jeffhanson.test:Name=HelloWorld,Type=GenericService 1156 [main] INFO com.jeffhanson.businesstier.services.HelloWorld - Hello everybody! |
서블릿 엔진이 서블릿을 중지하고 그 서블릿에 대해 destroy 메소드를 호출하면 서블릿은 Geronimo 커널을 중지한다. 서블릿 엔진이 서블릿을 중지하면 콘솔 아웃풋은 다음과 같다. (Listing 8)
Listing 8. 서블릿 중지 결과
1156 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel - Starting kernel shutdown 1156 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel - Kernel shutdown complete |
HelloWorld 클래스는 setMessage메소드, getMessage 메소드, sayHelloWorld 메시지를 가진 간단한 POJO이다. 이 클래스의 인스턴스를 Geronimo 커널과 함께 등록한 후에 인스턴스를 호출하고, 의존성 삽입을 사용하여 이를 다른 서비스와 컴포넌트들과 런타임 시 제휴시킬 수 있다. Listing 9의 코드는 간단한 HelloWorld POJO 클래스를 설명하고 있다.
Listing 9. HelloWorld 서비스
package com.jeffhanson.businesstier.services;
import org.apache.log4j.Logger;
public class HelloWorld
{
private static Logger log = Logger.getLogger(HelloWorld.class);
private String message = "Hello world";
public void setMessage(String message)
{
if (message == null || message.length() <= 0)
{
throw new RuntimeException("HelloWorld.setMessage "
+ "param is not set");
}
this.message = message;
}
public String getMessage()
{
return message;
}
public void sayHello()
{
log.info(message);
}
}
|
변화하는 비즈니스와 이벤트에 시의 적절하게 대응할 수 있는 민첩하고 효과적인 SOA를 디자인 하기란 여간 어려운 일이 아니다. 하지만 올바르게 설계된 POJO 레이어를 중심으로 구현된 SOA라면 가능하다. Geronimo 플랫폼은 POJO를 사용하여 유연하고 확장성 있는 SOA를 구현하는데 쓰이는 프레임웍과 툴을 제공한다.
| 설명 | 이름 | 크기 | 다운로드 방식 |
|---|---|---|---|
| SOA with POJOs framework | GeronimoSOAwithPOJOs.zip | 14KB | HTTP |
교육
-
fundamentals of POJO programming
-
SOA and dependency injection
-
MX4J
-
JMX
-
Java Reflection
-
Apache Geronimo project area
-
Get started now with Apache Geronimo
-
IBM® Support for Apache Geronimo
-
디벨로퍼웍스 오픈 소스 존
-
Apache articles와 free Apache tutorials
-
Safari bookstore
제품 및 기술 얻기
- Download Apache Geronimo, Version 1.0.
-
IBM trial software
-
IBM WebSphere® Application Server Community Edition V1.0
토론

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