iPOJO 튜토리얼 학술

iPOJO Tutorial (0.7.5) 번역
대충 의미가 통할 정도로 옮겼습니다.

이 페이지는 iPOJO 런타임을 사용하는 방법과, 이에 관련된 서비스 컴포넌트 모델을 다룹니다. 먼저 서비스 컴포넌트 모델의 개념을 소개한 뒤에 간단하게 iPOJO의 기능을 보여줄 수 있는 예제를 보겠습니다.

소개

iPOJO는 OSGi 프레임워크에 기반한 서비스 지향 프로그래밍(service-oriented programming)을 간단하게 만드는 것을 목표로 개발되었습니다. (iPOJO는 injected POJO의 약어입니다.) iPOJO는 OSGi 서비스 컴포넌트를 개발하는데 필요한 OSGi 관련 코드와 비기능적 측면들을 개발자 대신 처리해줌으로써 서비스 컴포넌트 구현을 간단하게 만들어줍니다. iPOJO 프레임워크는 개발자가 기능적 측면 (POJO)과 비기능적 측면 (의존성 관리, 서비스 준비, 설정 등)을 깔끔하게 분리할 수 있도록 해줍니다. iPOJO는 런타임에 기능 부분과 비기능 부분을 결합시킵니다. iPOJO는 POJO에 기반한 간단하면서도 확장성 있는 서비스 컴포넌트 모델을 제시합니다.

POJO 개념

POJO는 Plain Old Java Object의 약어입니다. 이 개념은 프레임워크 의존적인 코드가 가급적 원래 코드에 끼어들지 않도록 하는 것을 강조합니다. 객체가 전혀 어떤 특별한 존재가 아니라 그냥 일반적인 자바 객체라는 것을 강조하는데 사용하는 이름이지요.

Martin Fowler와 Rebecca Parsons, Josh Mackenzie가 2000년 9월에 이 용어를 만들어내면서 이렇게 말했습니다. "우리는 왜 사람들이 시스템에서 일반적인 객체를 사용하지 않는지 궁금했는데, 그 이유가 부르기 좋은 이름이 없는데 있다고 결론내렸다. 그래서 POJO라는 용어를 만들었는데 아주 잘 먹혔다."

iPOJO 프레임워크는 개발자 관점에서 봤을 때 POJO만 사용할 수 있도록 최대한 노력을 기울였습니다.

iPOJO 서비스 컴포넌트 개요

서비스 컴포넌트는 서비스를 제공하거나 사용할 수 있습니다. 서비스는 주어진 서비스 인터페이스를 구현하는 객체입니다. 서비스 인터페이스는 자바 인터페이스로 구현됩니다. 서비스를 제공하거나 사용하는 컴포넌트를 만드는게 iPOJO 서비스 컴포넌트 모델의 핵심입니다. iPOJO는 여기에 컴포넌트의 상태 변화를 알려주는 콜백 개념을 추가로 도입했습니다.

컴포넌트는 iPOJO에서 가장 핵심적인 개념입니다. iPOJO 모델에서 컴포넌트는 서비스 의존성, 제공하는 서비스, 콜백을 기술합니다. 이 정보들은 컴포넌트의 메타데이터로 기록되구요. 두번째로 중요한 개념은 컴포넌트 인스턴스입니다. 컴포넌트 인스턴스는 컴포넌트의 특정한 버전입니다. iPOJO 런타임은 메타데이터와 인스턴스 설정을 모두 이용하여 컴포넌트를 관리합니다. (예를 들면 생명주기 관리, 서비스 인젝션, 서비스 게시, 필요 서비스 검색 등)

간단한 예제

이제 핵심적인 iPOJO 개념을 잘 설명하는 예제를 보도록 하겠습니다. 이번 예제는 두 개의 컴포넌트로 구성되는데, 하나는 Hello 서비스이고, 나머지 하나는 임의의 갯수의 Hello 서비스를 사용하는 컴포넌트입니다. 이 컴포넌트들은 Maven을 이용해서 두 개의 번들로 패키징됩니다. Hello 서비스 번들은 서비스 인터페이스와, 이 서비스를 구현하는 컴포넌트로 구성되어 있습니다. Hello 서비스를 이용하는 번들은 Hello 서비스를 이용하는 컴포넌트만 포함하고 있습니다.

Maven 준비

먼저 Maven을 다운로드하고 설치합니다. 이 예제는 Maven 2.0.4를 기준으로 하고 있습니다. Maven을 설치한 이후에는 Felix를 다운로드하고 설치합니다. iPOJO는 Felix 프로젝트의 일부분입니다. 여기서 다운로드 하면 됩니다.

Hello 서비스 제공자

먼저 Hello 서비스 제공자에 필요한 뼈대를 구성하도록 Maven을 이용해서 프로젝트를 생성합시다. 아래 명령을 치세요.
mvn archetype:create -DarchetypeGroupId=org.apache.felix \
-DarchetypeArtifactId=maven-ipojo-plugin -DarchetypeVersion=0.7.5-SNAPSHOT \
-DgroupId=ipojo.example -DartifactId=hello.impl \
-DpackageName=ipojo.example.hello.impl
이렇게 치면 현재 디렉터리에 "hello.impl"이란 프로젝트 디렉터리가 생성됩니다. 프로젝트 디렉터리는 "pom.xml"과 "src" 디렉터리를 포함하고 있습니다. "pom.xml"은 Maven의 프로젝트 객체 모델 (POM)로, Maven 프로젝트를 기술하는데 사용됩니다. 이클립스에서 m2eclipse 플러그인을 설치하고, Import를 이용해서 Maven Project를 불러오면 됩니다.

이제 Hello 서비스 인터페이스를 "src/main/java/ipojo/example/hello/Hello.java" 파일에 만들어봅시다.
package ipojo.example.hello;
public interface Hello {
/**
* Return a message like: "Hello $user_name"
* @param name: the name of the user
* @return the hello message
**/
public String sayHello(String name);
}
이 서비스의 컴포넌트 구현은 단지 Hello 서비스 인터페이스를 구현하는 자바 클래스일 뿐입니다. "src/main/java/ipojo/example/hello/impl/HelloImpl.java" 파일을 만들고 거기에 아래와 같이 서비스 구현을 써넣읍시다.

package ipojo.example.hello.impl;

import ipojo.example.hello.Hello;

/**
* Component implementing the Hello service.
**/
public class HelloImpl implements Hello {
public String sayHello(String name) { return "hello " + name; }
}
iPOJO가 이 컴포넌트를 관리하려면 Hello 서비스에 관련된 메타데이터가 필요합니다. "src/main/resources/metadata.xml" 메타데이터 파일에 아래와 같이 씁니다. (아래와 같은 형식 대신 JAR 매니페스트 파일에 쓰는 방법도 있습니다.)

<?xml version="1.0" encoding="UTF-8"?>
<iPOJO>
  <component className="ipojo.example.hello.impl.HelloImpl">
    <provides/>
  </component>
  <instance component="ipojo.example.hello.impl.HelloImpl" name="HelloService"/>
</iPOJO>

빨갛게 표시한 부분이 편집할 부분입니다. 위와 같은 XML 기반 메타데이터 형식에서는 component 엘리먼트가 className 속성을 반드시 가지고 있어야 합니다. 이 속성은 iPOJO에게 컴포넌트의 구현 클래스를 알려줍니다. 이 예제의 컴포넌트는 서비스를 제공하기 때문에, 컴포넌트 엘리먼트가 자식 엘리먼트 <provides />를 가지고 있습니다. provides 엘리먼트는 iPOJO에게 서비스 게시를 지시합니다. provides 엘리먼트가 별도의 interface 속성을 가지고 있지 않기 때문에, iPOJO는 컴포넌트가 구현하고 있는 모든 서비스 인터페이스를 노출합니다. (일부 서비스 인터페이스만 선택적으로 게시할 수도 있습니다.) instance 엘리먼트는 iPOJO에게 컴포넌트가 시작되었을 때 인스턴스를 생성하도록 지시합니다.

마지막으로, 프로젝트 디렉터리에 있는 "pom.xml" 파일을 아래와 같이 편집하도록 합시다. 원문에서는 하이라이트 되어있지 않지만 <packaging>bundle</packaging> 부분이 jar로 되어 있는 경우에는 bundle로 고쳐야 합니다.

<project>
  <modelVersion>4.0.0</modelVersion>
 <packaging>bundle</packaging>
  <groupId>ipojo.example</groupId>
  <artifactId>hello.impl</artifactId>
  <version>0.0.1</version>
  <name>Hello Service</name>
  <pluginRepositories>
    <pluginRepository>
      <id>apache.snapshots</id>
      <name>snapshot plugins</name>
      <url>
        http://people.apache.org/repo/m2-snapshot-repository
      </url>
      <releases>
        <enabled>false</enabled>
      </releases>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </pluginRepository>
  </pluginRepositories>
  <build>
   <plugins>
     <plugin>
       <groupId>org.apache.felix</groupId>
       <artifactId>maven-bundle-plugin</artifactId>
       <extensions>true</extensions>
       <configuration>
         <instructions>
           <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
           <Private-Package>ipojo.example.hello.impl</Private-Package>
           <Export-Package>ipojo.example.hello</Export-Package>
         </instructions>
       </configuration>
     </plugin>
     <plugin>
             <groupId>org.apache.felix</groupId>
             <artifactId>maven-ipojo-plugin</artifactId>
             <version>0.7.5-SNAPSHOT</version>
                 <executions>
               <execution>
               <goals>
                     <goal>ipojo-bundle</goal>
              </goals>
         </execution>
       </executions>
     </plugin>
   </plugins>
 </build>
</project>

POM 파일에서 빨간색 부분을 편집합니다. POM 앞부분은 프로젝트 정보 (name, groupId, artifactId)와 더불어 번들로 패키징할 것을 지시하고 있는데 원래 maven에서 사용되는 부분이고, 아래 부분에 OSGi 번들 설정이 있습니다. 번들 이름, 익스포트/임포트할 패키지 등 번들 설정이 들어갑니다. 서비스 제공자 번들은 Hello 인터페이스를 담은 패키지를 익스포트해야 합니다.

"pom.xml" 파일 편집이 끝났으면 maven 명령을 내려서 프로젝트를 빌드하도록 합시다.

mvn clean install

그러면 Maven이 빌드하면서 결과를 보고합니다. 성공하면 프로젝트의 target 디렉터리에 번들 JAR 파일이 생성되고, 로컬 maven 저장소에 JAR 파일이 설치됩니다.

Hello 서비스 클라이언트

아까처럼 Hello 서비스 클라이언트 프로젝트 뼈대를 생성합시다. 아래 명령을 이용합니다.
mvn archetype:create -DarchetypeGroupId=org.apache.felix \
-DarchetypeArtifactId=maven-ipojo-plugin -DarchetypeVersion=0.7.5-SNAPSHOT \
-DgroupId=ipojo.example -DartifactId=hello.client \
-DpackageName=ipojo.example.hello.client
현재 디렉터리에 "hello.client" 프로젝트 디렉터리가 생성됩니다. 이 프로젝트 디렉터리 안에 "pom.xml"과 "src" 디렉터리가 있습니다.

이제 "src/main/java/ipojo/example/hello/client/HelloClient.java" 파일을 만들고 아래 내용을 입력합니다.
package ipojo.example.hello.client;

import
ipojo.example.hello.Hello;

public class HelloClient implements Runnable {

private Hello[] m_hello; // Service Dependency
private final static int DELAY=10000;
private boolean end;

public void run() {
while (!end) {
try {
invokeHelloServices();
Thread.sleep(DELAY);
} catch (InterruptedException ie) { }
/* will recheck end */
}
}

public void invokeHelloServices() {
for (int i = 0; i < m_hello.length; i++) {
System.out.println(m_hello[i].sayHello("Clement"));
}
}

public void starting() {
Thread
T = new Thread(this);
 end = false;
 T.start();
}

public void stopping() {
end = true;
 }
}
Hello 서비스 클라이언트는 스레드를 만들고 주기적으로 사용 가능한 Hello 서비스를 호출합니다. 스레드는 최소한 하나의 Hello 서비스 제공자가 존재하게 되면 iPOJO의 콜백을 통해 실행됩니다. Hello 서비스 컴포넌트를 사용하려면 그냥 클라이언트 코드에서 서비스 타입으로 필드 선언만 하고 바로 쓰면 땡입니다. 이 예제에서는 m_hello 필드를 선언하여 사용하고 있습니다. (Hello 배열) iPOJO에서 서비스의 배열은 다중 의존성을 표현하는데 사용하고, 스칼라 값은 단일 의존성을 표현하는데 사용합니다. 다시 말하면, 단일 의존성의 경우 위의 예에서 배열 괄호만 제거해주면 됩니다. (예: HelloService m_hello[]. 서비스 필드를 선언한 다음에 컴포넌트 코드에서 서비스 필드가 초기화 되었다고 가정하고 다음과 같이 사용할 수 있습니다. m_hello[i].sayHello("Clement").

iPOJO가 서비스 동기화도 관리하기 때문에 서비스를 호출할 때 별도로 동기화 블럭을 사용할 필요도 없습니다.

이 컴포넌트는 두 개의 콜백 메소드를 정의하고 있습니다. (starting/stopping) 콜백은 컴포넌트 상태가 변화될 때 호출됩니다. iPOJO에서는 컴포넌트 상태가 INVALID (모든 컴포넌트 제약 조건이 만족되지 않은 상태) 이거나 VALID (모든 컴포넌트 제약 조건이 만족된 상태) 입니다. 이 예제에서는 starting() 콜백이 스레드를 만들고 시작시킵니다. stopping() 콜백은 스레드를 중지시킵니다. 컴포넌트 메타데이터에 iPOJO가 VALID <-> INVALID 상태 전환시 이 콜백 메소드를 호출하도록 설정하게 되어있습니다.

"src/main/resource/metadata.xml" iPOJO 메타데이터 파일을 아래와 같이 편집합니다.

<?xml version="1.0" encoding="UTF-8"?>
<iPOJO>
  <component className="ipojo.example.hello.client.HelloClient">
    <requires field="m_hello"/>
    <callback transition="validate" method="starting"/>
    <callback transition="invalidate" method="stopping"/>
  </component>
  <instance component="ipojo.example.hello.client.HelloClient" name="HelloClient"/>
</iPOJO>

빨간 부분을 편집하면 됩니다. component 엘리먼트는 className 속성을 가지고 있는데, 컴포넌트 구현 클래스를 가리킵니다. Hello 서비스 의존성은 requires 엘리먼트와, 이 엘리먼트의 field 속성에 멤버 이름을 써주는 것으로 표현합니다. callback 엘리먼트는 컴포넌트 상태 변화가 발생할 때 호출될 메소드를 지정합니다. 마지막 instance 엘리먼트는 iPOJO에게 컴포넌트의 인스턴스를 하나 만들 것을 지시합니다.

마지막으로 "pom.xml" 파일을 아래와 같이 편집합니다.

<project>
  <modelVersion>4.0.0</modelVersion>
  <packaging>bundle</packaging>
  <groupId>ipojo.example</groupId>
  <artifactId>hello.client</artifactId>
  <version>0.0.1</version>
  <name>Hello Client</name>
  <dependencies>
    <dependency>
      <groupId>ipojo.example</groupId>
      <artifactId>hello.impl</artifactId>
      <version>0.0.1</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  <pluginRepositories>
    <pluginRepository>
      <id>apache.snapshots</id>
      <name>snapshot plugins</name>
      <url>
        http://people.apache.org/repo/m2-snapshot-repository
      </url>
      <releases>
        <enabled>false</enabled>
      </releases>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </pluginRepository>
  </pluginRepositories>
  <build>
     <plugins>
     <plugin>
     <groupId>org.apache.felix</groupId>
     <artifactId>maven-bundle-plugin</artifactId>
     <extensions>true</extensions>
     <configuration>
       <instructions>
         <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
         <Private-Package>ipojo.example.hello.client</Private-Package>
         <Import-Package>*</Import-Package>
       </instructions>
     </configuration>
   </plugin>
   <plugin>
           <groupId>org.apache.felix</groupId>
           <artifactId>maven-ipojo-plugin</artifactId>
           <version>0.7.5-SNAPSHOT</version>
           <executions>
             <execution>
             <goals>
                   <goal>ipojo-bundle</goal>
            </goals>
             </execution>
         </executions>
   </plugin>
 </plugins>
  </build>
</project>

빨간 부분을 편집하면 됩니다. dependencies 엘리먼트는 Maven에게 컴파일 의존성을 알려주기 위해 사용합니다. (없으면 컴파일 할 때 에러 발생. 런타임에 가능하더라도.) 이 경우에는 클라이언트 번들이 컴파일되려면 Hello 서비스 인터페이스를 필요로 합니다. 아까 Hello 서비스 제공자 번들 JAR을 로컬 Maven 저장소에 설치했는데, dependencies에서 지정해놓으면 로컬에서 JAR 파일을 찾아 쓸 수 있게 되는 것입니다.

"pom.xml" 파일 편집이 끝나면 아래 명령을 쳐서 빌드합니다.

mvn clean install

아까와 마찬가지로 번들 JAR 파일이 target 디렉터리에 생성되고, 로컬 Maven 저장소에 설치됩니다.

예제 실행

이제 Felix (OSGi 구현체)를 이용해서 예제를 돌려볼 때가 되었습니다.

java -jar bin/felix.jar

Felix 명령 쉘 프롬프트에서 iPOJO 런타임 번들, Hello 서비스 제공자 번들, 클라이언트 번들을 순차적으로 설치합니다.

start file:/<ipojo-directory>/org.apache.felix.ipojo-0.7.5-incubator-SNAPSHOT.jar
start file:/<hello-service-project-directory>/target/hello.impl-0.0.1.jar
start file:/<hello-client-project-directory>/target/hello.client-0.0.1.jar
설치와 동시에 번들이 시작됩니다. 번들이 시작됨과 동시에 터미널에서 Hello Clement가 10초마다 출력되는걸 확인할 수 있습니다.

트랙백

이 글과 관련된 글 쓰기 (트랙백 보내기)
TrackbackURL : http://xeraph.com/tb/4630417 [도움말]

덧글

댓글 입력 영역