BLOG main image
OSGi Story (31)
Hot Issue (11)
Equinox (9)
Spring-OSGi (0)
J2ME (7)
OSGi-UFK (2)
Visitors up to today!
Today hit, Yesterday hit
daisy rss
tistory 티스토리 가입하기!
2007. 11. 20. 11:20

HttpLogService 이용한 Bundle 구현


WSDD (WebSphere Studio Device Developer)
이용하여 HttpLog라는 번들을 구현해보기로 한다. HttpLog번들은 특정한 HttpServlet 의해 엑세스된 트랜젝션의 로그 메시지를 추적하고 기록하는 기능을 갖는다.

 

/// Step 1. Bundle skeleton 생성

WSDD에서 자바 프로젝트를 통해서 HttpLog라는 번들 스켈레톤을 만든다. 또한 Properties 통해 OSGi framework library osgi.jar servlet.jar build path 수정한다.


/// Step 2.
OSGi BundleActivator interface 구현

이제 HttpLogBundleActivator 라는 자바 클래스를 작성하면서 OSGi BundleActivator interface 구현해보기로 한다. BundleActivator interface 두개의 메소드를 정의하는데 start() stop() 바로 그것이다. start() method 번들이 active OSGi System  Framework로부터 invoke된다. 아래의 코드에서 BundleContext argument delegation object Framework로부터 invoke될때에 구동되는 Operation set이다.

//HttpLogBundleActivator.java

public class HttpLogBundleActivator implements BundleActivator {

            

             BundleContext bc;

             public void start(BundleContext context) throws Exception {

                           bc=context;

                           //to add initialization code here

                          

             }

             public void stop(BundleContext context) throws Exception {

                                                    

             }

            

}

 

/// Step 3. Register custom service

이제 HttpLogService라는 로그기록을 관리하는 custom service 구현하기로 한다. 3개의 자바 파일을 코딩하는데, 명칭은 각각 다음과 같다. HttpLogService.java, HttpLogServiceImpl.java, HttpLogServiceFacotry.java. 우리는 custom service 위한 하나의 interface class implementation class 구현하는데 예를 들어 HttpLogService interface 경우 log() 라는 하나의 메소드를 정의한다.

//HttpLogService.java

package httplog;

 

public interface HttpLogService {

             void log(String message);

}

 

//HttpLogServiceImpl.java

package httplog;

public class HttpLogServiceImpl implements HttpLogService{

             static public List messageArray = new ArrayList();

            

             public void log(String message) {

                           HttpLogServiceImpl.messageArray.add(message);

             }

            

}


일반적으로
어떤 custom services 요청하는 번들이(hosting bundle) active될때에, 해당 custom services System Framework 등록이 된다. 따라서 Step2에서 구현했던 HttpLogBundleActivator 클래스에 아래의 start() method 추가한다.

//HttpLogBundleActivator.java

public class HttpLogBundleActivator implements BundleActivator {      

static final String httpLogServiceName = HttpLogService.class.getName();

            

public void start(BundleContext context) throws Exception {

                           registerHttpLogService();

             }           

private void registerHttpLogService(){

                           HttpLogServiceFactory sf = new HttpLogServiceFactory();

                           ServiceRegistration sr=bc.registerService(httpLogServiceName,sf,null);

             }

}


또한
registerService() 호출될 , 해당 번들은 정의된 custom service name OSGi ServiceFactory interface 서비스 오브젝트 그리고 해당 서비스의 properties 포함된 Dictionary object 제공한다. 각각의 ServiceFactory subclass getService() ungetService()라는 두개의 메소드를 갖게된다. 우리가 작성하는 코드에서는 sf 하나의  HttpLogServiceFactory 타입이면서 또한 HttpLogServiceImpl instances generate하는 역할을 한다.

//HttpLogServiceFactory.java

package httplog;

import org.osgi.framework.Bundle;

import org.osgi.framework.ServiceFactory;

import org.osgi.framework.ServiceRegistration;

 

public class HttpLogServiceFactory implements ServiceFactory{

             public Object getService(Bundle bun, ServiceRegistration sr) {

                           return new HttpLogServiceImpl();

             }

 

             public void ungetService(Bundle bun, ServiceRegistration sr, Object obj) {

             }

}

 

/// Step 4. Consume external service

우리가 작성하는 HttpLog Bundle 의해서 제공되는 외부서비스는 바로HttpService이다. 사용자는 localhost HttpServlet 억세스한 기록들을 HttpLog bundle 작성한 레코드를 통해서 추적할 수가 있다. 이러한 기능을 제공하기위해서 HttpLog bundle HttpService 제공하는 인터페이스를 통해서 HttpService bundle servlet object 등록해야만 한다. HttpLog bundle start 위의 과정이 자동적으로 일어나게된다.

public class HttpLogBundleActivator implements BundleActivator {

            

             static final String servletURI ="/httplog";

             private void initialize(){

                          

                           //Import HttpService

                           attachToHttp();

                           //register HttpLogService

                           registerHttpLogService();

 

             }

             private void attachToHttp(){

                           httpContext = new HttpContext(){

 

                              public boolean handleSecurity(HttpServletRequest request,

                                                                                                          HttpServletResponse response)

                              {

                                           return true;

                              }

 

                              public URL getResource(final String name)

                              {

                                           return (URL) AccessController.doPrivileged(new PrivilegedAction()

                                           {

                                                        public Object run()

                                                        {

                                                                     String resource = name;

                                                                     if (resource.charAt(0) != '/')

                                                                                   resource = "/".concat(resource); //$NON-NLS-1$

                                                                     return getClass().getResource(resource);

                                                        }

                                           });

                              }

 

                              public String getMimeType(String name)

                              {

                                           return null;

                              }

                                       

                           };

 

                           ServiceReference httpSR = bc.getServiceReference(HttpService.class.getName());

                           HttpService http = (HttpService)bc.getService(httpSR);

            

                           try {

                                        http.registerServlet(servletURI, new HttpLogServlet(), null, httpContext);

                          

                           } catch (ServletException e) {

                                        e.printStackTrace();

                           } catch (NamespaceException e) {

                                        e.printStackTrace();

                           }

             }

            

}

 

//HttpLogServlet.java

package httplog;

 

import java.io.IOException;

import java.io.PrintWriter;

import java.util.List;

 

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.ServletException;

 

public class HttpLogServlet extends HttpServlet{

 

             protected void doGet(HttpServletRequest req, HttpServletResponse res)

                            throws ServletException, IOException

              {

                            res.setContentType("text/html; charset=UTF-8");

                            PrintWriter out = res.getWriter();

 

                            out.print("<html><head><title>" + "Http Log Display" + "</title></head>");

                            out.print("<body text=\"#000000\" bgcolor=\"#C0C0C0\"

                                         link=\"#0000EE\" vlink=\"#551A8B\" alink=\"#FF0000\">");                    

                            List msgArray = HttpLogServiceImpl.messageArray;

                            

                            int len=msgArray.size();

                            for (int i=0;i<len;i++){

                                        String msg= (String)msgArray.get(i);

                                        out.print(msg);

                            }

                            out.println("</body></html>");

              }

 

}


위의
코드를 살펴보면 번들이 초기화될 특정한 HttpServlet object HttpService 등록되게 된다. 일반적으로 external service 구동되기전에 요청 번들은 OSGi System Framework로부터 external service instance 어디에 위치해있는지 필요하게 된다. 이러한 일련의 과정은 BundleContext에서 getServiceReference() getService() 메소드를 제공하므로 수행된다. 두개의 메소드는 필요로 하는 정보들을 service registry에서 찾아서 리턴하는 역할을 한다.

 

/// Step 5. Migrate to bundle format

코드를 모두 작성하고나면 실제 번들을 패키지 형태로 작성하는 과정을 거치게된다. WSDD에서File->New->SMF 하고 Bundle Folder 선택하여 새로운 번들을 생성한다. target bundle folder에서HttpLog 선택한다. 나머지 옵션은 선택하지않는다. 다음 단계로 MANIFEST 작성할 단계이다. MANIFEST.MF 파일은 번들들의 독립성과 정보 공유 협업을 위해서 매우 중요한 정보 파일이다. 반드시 일정한 OSGi specification 의해서 작성되어야 한다. WSDD MANIFEST editor 그림처럼 사용자가 쉽게 작성할수있도록 도움을 준다.

 

다음의 섹션을 필수적으로 정의해야하는 항목들이다. 다음은 우리가 작성한 예제파일의 경우이다.


- Bundle-Name : HttpLog.

- Bundle-Version : 1.0.0.

- Bundle-Activator : httplog.HttpLogBundleActivator

- Import Services : org.osgi.service.HttpService

- Import Packages : javax.servle, javax.servlet.http, org.osgi.framework, org.osgi.service.http.


OSGi
하나의 JVM 환경에서 작동한다. 그러나 하나의 VM이라고 하나의 프로세스 또는 싱글 태스크라고 생각하면 잘못된것이다. 앞서도 이야기했지만, OSGi 비록 하나의 VM위에서 구동하더라도 번들간의 독립성과 협업을 하기위해서 번들마다 서로 다른 클래스 로더가 구동하여 작동하게 되어있다. 따라서 번들은 MANIFEST file에서 자신의 클래스 패스와 버전, 기본적인 정보들을 레퍼런스 하게된다.


- Export Packages : httplog.

- Export Services : httplog.HttpLogService.


MANIFEST.MF 파일까지 생성하게되면 이제 번들을 배포할 준비는 모두 마치게 된다. 번들의 배포는 .jar 패지키 형태로 생성하게되며 우리는 httplog.jar 명칭으로 생성한다. WSDD Project explorer view에서
HttpLog project Export하면 된다.

 

/// Step 6. OSGi Server Bundle 배포

우리가 작성한 번들을 배포하려면 OSGi System Framework 탑재된 시스템을 찾고 그곳에 추가하면 된다. WSDD에서는 이런 모든 과정을 매우 쉽게 접근할수있게 도와주는데, 다음의 방법을 사용한다. 우선 [Run] Window 실행시켜서 OSGi 서버(SMF Server) 생성하고 구동한다. 이렇게 SMF Server 구동한후에, Project Explorer에서 [HttpLog project] 클릭하고 SMF->Submit Bundle 선택하면 번들의 배포는 끝난다. SMF server 구동시에 서버가 되는 타겟에 대한 대화상자가 나오는데, 그곳에 아이디와 패스워드, 아이피등에 대한 기본적인 억세스 정보들을 적는다. 우리는 로컬PC에서 작업을 하기에 Admin@localhost:8080/smf 기입한다. 이렇게 모든 서버의 구동과 번들의 배포가 마친후에 [SMF perspective]-[SMF Bundle Servers view]에서 HttpLog 번들이 SMF server 배포된 것을 확인할 수가 있다. IBM 솔루션에서는 이러한 모든 과정들이 WSDD Dialog Box에서 자동적으로 이루어지지만, 다른 여타의 OSGi 솔루션에서는 각기 다른 방법으로 로컬 서버 원격 서버로(실제 OSGi 설치된 PC 타겟 디바이스) 배포할 수가 있다.


/// Step 7. Bundle testing

이제 우리가 구현한 번들을 배포했고, 테스트하는 단계만 남았다. 역시도 테스트 번들을 구현하기로 한다. 테스트 번들을 HttpLogTester 명명하고, 번들은 테스트용으로 HttpLogService invoke하는 것이 주된 기능이다. 번들이 HttpLogService invoke하면 다음 단계로 Http log 화면에 정상적으로 나타난다면 우리가 만든 번들은 성공적으로 작성한것으로 본다. 번들 역시 우리가 거쳐왔던 step 1,2 반복하여 bundle skeleton 생성한다. 빌드 패스에 httplog.jar 포함하며 아래의 코드와 같이 HttpLogTesterBundleActivator start() method 코딩한다.

//HttpLogTesterBundleActivator.java

class HttpLogTesterBundleActivator implements BundleContext{

             public void start(BundleContext context) throws Exception {

                           bc=context;

                           ServiceReference httpLogSR=bc.getServiceReference(httpLogServiceName);

                           HttpLogService httpLog =(HttpLogService)bc.getService(httpLogSR);

                           httpLog.log("Hello world!");

             }

}


step 2-6
반복하며 마지막으로 역시 HttpLogTester 번들을 SMF server 배포한다.

 

/// Step 8. Test HttpLog in SMF runtime

이제 우리는 배포된 HttpLogTester 번들을 실제 install – activate 시키는 과정을 거쳐서 우리가 만든 번들이 제대로 작동되는지 확인해야한다. SMF Runtime 실행시키고 HttpLogTester install 한다. 모든 과정 역시 WSDD SMF Bundle Server view에서 지원된다. HttpLogTester 번들이 SMF Bundle Server install 되는 순간 필요한 번들을 요청하게되어 HttpLog HttpService 자동적으로 함께 install 되고 ready하게 된다. 이제 로그 메시지를 확인하기위해서 웹브라우저에 http://localhost/httplog라고 타입하면 우리는 브라우저에서 해당 로그 결과를 살펴볼수가 있게 된다.

 

이번호에서 우리는 OSGi bundle 기본적인 구조와 코드를 통해서 구현과 배포, 실행에 대해서 살펴보았다. 예제 코드에서 로컬 서버와 번들간의 인터페이스들을 살펴보았지만, 실제 구현되는 코드에서는 DB와의 연동과 원격관리서버를 통한 업데이트와 관리 그리고 마지막으로 네이티브 코드(Native Code)와의 연동을 통해서 OSGi 장점을 최적화하여 사용하고 있다. 특히 방금 이야기한 DB handling & Sync, Bundle Remote Management & Dynamic Update, JNI 통한 Native code interface 등은 OSGi 고급 핵심 기술들로 향후 더욱 많이 사용될 중요한 부분이다. 여타의 OSGi 솔루션들도 마차가지겠지만, 특히 IBM OSGi 솔루션은 Eclipse Plugin Framework – Equinox, Business Process Application – Expeditor등을 거쳐서 더욱 강력한 Middleware Framework 부각되고 있다. 다음호에서는 RCP(Rich Client Platform) 대변되는 Eclipse OSGi Framework “Equinox” 통해서 임베디드에서 데스크탑 애플리케이션, 그리고 플랫폼의 일부분으로까지 확장되어가는 OSGi 사례를 살펴본다.

 

/// 참고문헌

1. Managed mobile clients with OSGi - http://www.ibm.com/developerworks/library/wi-osgi

2. IBM Workplace Client Technology, Micro Edition and Open Services Gateway Initiative (OSGi) –http://www.ibm.com/developerworks/websphere/library/techarticles/0606_salkosuo

/0606_salkosuo.html

3. IBM Service Management Framework - http://www-306.ibm.com/software/wireless/smf/index.html