네번째 마당.
CORBA 활용 방안 및 미래
7막 CORBA 구축 가이드
수년간 국내의 전산 환경을 접하면서 항상
느끼는 점은 대다수의 개발자들이 새로운 프로젝트를 추진하거나 기존의
시스템을 업그레이드할 때 개발될 시스템에 대한 기대때문인지는 몰라도
기존의 시스템이나 적용하려는 실제 상황에 대해서는 소홀히 다룬다는
사실이다.
실제 이러한 실수로 인해 불필요한 경비를
지출하는 사례가 허다하다. 예를
들면, 현재
데이타와 향후 예상되는 데이타를 처리하는 데 충분한 데이타베이스
시스템으로는 I사의
제품이면 충분함에도 굳이 비싼 가격의 다른 제품을 구입하고 해당
제품의 기능을 10%도
활용하지 못하는 경우가 허다하다. 이러한
상황의 배경에는 " 어차피 내 돈도 아닌데 뭐,
어차피 나중에 괜히 문제가 생기면
골치 아프니까 최고로 기능이 많고 비싼 걸로 사지 뭐!
" 라는 식의 분위기도 있겠지만
그 배경에는 구축하려는 환경을 정확히 이해하지 못하는데 있다.
왜냐하면 정상적인 엔지니어가 현재의
구축 환경을 정확히 이해하고 있다면 당연히 그와 같은 과소비 구매는
하지 않을 것이기 때문이다.
CORBA는 작은 규모의
환경보다는 대규모 분산 환경에 적합하다. CORBA를
적은 규모에 는 적용할 수 없다라는 말은 아니다.
단지 다양한 하드웨어와 소프트웨어로
이루어진 대규모 분산 환경에서 더욱 진가를 발휘한다는 의미이다.
따라서 이러한 대규모 시스템을
구축하려면 가장 중요한 부분이 현 상황을 분석하는 과정이다.
이 과정의 성패가 시스템 구축의
성패를 좌우해도 과언은 아니다. 따라서
7막에서는
CORBA를 사용하는
이유를 비롯하여 사용 시나리오 , 구축
방법론 등 대규모 시스템 구축에 필요한 사항들에 대해 정리해 본다.
1장 CORBA를
사용하는 이유
앞서 CORBA 표준에
대해 자세히 살펴보았다. 그렇다면
도대체 CORBA를
사용하는 이유가 무엇일까? 다시
말해, CORBA를
사용해서 얻는 이득은 무엇일까? 실제
CORBA를
사용하여 얻는 이득을 시스템 개발자와 개발된 시스템을 이용하는
사용자 측면에서 살펴보자.
CORBA는 시스템
통합 기술이기 때문에 복잡한 시스템들을 처리해야 만 하는 시스템
개발자들이 가장 많은 효과를 얻는다. 이들이
얻을 수 있는 효과는 다음과 같다.
시스템을 새롭게 디자인하거나 기존의
시스템을 확장할 경우, 가장
문제가 되는 부분이 바로 기존의 시스템들을 어떻게 사용할 것인가
하는 것이다. 실제로,
기존에 구축되어 있는 관계형
데이타베이스의 재사용이나 사용하던 윈도우 툴의 재사용 문제들은
가장 빈번히 발생하는 상황이다. 이러한
상황에서 CORBA는
기존의 시스템을 변경하지 않고 그대로 사용하게 해 준다.
이는 CORBA의
IDL을 이용하여
기존 시스템과 통신을 하기위한 껍데기 객체를 작성함으로서 쉽게
구현할 수 있다. 이러한
기술을 'CORBA 포장(Wrapping)
기술' 이라 한다(
이 기술에 대해서는 Mowbray의
'The Essential CORBA'라는
책에서 자세히 다루고 있다).
시스템 개발자들은 CORBA객체를
다루기 위해 여러가지 기능을 필요로 한다. 가령,
데이타베이스나 트렌젝션 ,
동시성 제어 등은 복잡한 시스템을
구축할 때 필수적인 기능이다. 시스템
개발자는 이러한 기능들을 일일이 작성할 필요가 없다.
왜냐하면 CORBA
는 CORBA
객체 서비스를 통해 CORBA객체를
다루는 데 필요한 서비스들을 제공하기 때문이다.
시스템 개발자들은 CORBA
서비스를 통해 고도의 생산성을
보장받을 수 있다.
CORBA 응용 시스템을 구성하는 객체들은 IDL이라는 인터페이스 정의어를 통해 정의된다. 이 들 객체는 IDL 컴파일러에 의해서 다양한 구현 언어로 변환된다. 비록 CORBA 객체들이 서로 다른 구현 언어로 작성되더라도 이 들 객체는 서로 간에 서비스를 교환할 수 있다. 왜냐하면 CORBA에서 지원하는 공통의 인터페이스를 사용하기 때문이다. 예를 들어 C++로 작성된 CORBA 클라이언트 객체와 자바로 작성된 CORBA 구현 객체는 공통의 인터페이스를 사용하여 통신할 수 있다. 이처럼 CORBA를 사용하면 구현 언어에 상관없이 작성된 응용 프로그램인 컴포넌트를 재사용할 수 있다.
또한 IDL 자체가 객체지향 개념을 지원하기 때문에 상속 등을 통해 IDL로 정의한
인터페이스를 재사용할 수 있다.
CORBA는 700여
개가 넘는 회사들에 의해서 합의된 산업계 표준이다.
뿐만 아니라 IDL
언어는 ISO국제
표준으로 제정될 전망이다. 따라서
CORBA를
사용한 시스템이라는 의미는 국제 표준을 따른 시스템이라는 것이다.
결국 개발자는 CORBA
표준이 제공하는 통일성,
확장성,
개방성 등 다양한 이점을 얻을
수 있다.
시스템 개발자 뿐만 아니라 구축된 시스템을
사용하는 사용자와 사용 기관들도 CORBA를
통해 많은 이득을 얻을 수 있다. 다음은
대표적인 이득이다.
CORBA는 산업계
표준이기 때문에 빠른 시간에 저렴한 투자 비용으로 경쟁력 있는 시스템을
확보할 수 있는 방법을 제공한다.
구현된 CORBA 클라이언트
객체와 구현 객체는 네트웍상의 어느 곳에서라도 위치할 수 있다.
따라서 사용자의 요구사항에 맞춰
다양한 규모로 시스템을 구성할 수 있을 뿐만 아니라 복 수의 소프트웨어로
부터 컴포넌트를 구성하고 이를 적절히 배치하여 활용할 수 있다.
복잡하고 다양한 시스템들을 CORBA를
기반으로 통합함으로써 관리 및 운영이 쉬어지며 경비도 적게 든다.
2장 어떤
CORBA제품을
선택할 것인가?
앞 막에서 살펴본 것처럼 CORBA를
구현한 많은 제품들이 존재한다. 따라서
이들 제품들 중에서 가장 적합한 제품을 선택하기 위한 지식이 필요하다.
물론, 절대적인
가이드는 없지만 이들 제품이 CORBA를
구현한 것이기 때문에 얼마나 CORBA 표준을
충실히 반영하는 가하는 점과 여러분이 구축할 환경하에서 필요로
하는 서비스들을 충실히 지원하는 가가 중요한 요소로 작용한다.
다음은 이 들 고려 사항들을 정리한
것이다.
가장 먼저 고려해야 할 사항은 해당 제품이
얼마나 충실히 CORBA 표준을
지원하는 가이다. 다음은
점검해야 할 항목들이다.
1. CORBA 코아 기능을 얼마나 충실히 지원하는 가?
2. ORB 구성요소를 얼마나 충실히 지원하는 가?
CORBA 표준에 대한
고려 사항 외에 추가적인 고려 사항이 필요하다.
예를 들면,
기존의 구축된 시스템이 다른
회사의 CORBA 제품이라면
기존의 시스템과 새로운 시스템을 연동하기 위해서 IIOP
지원여부가 중요한 선택 요소일
것이다. 또한
기존의 시스템이 DCE로
구축된 시스템이라면 새로운 CORBA제품은
DCE ESIOP를
지원해야 할 것이다. 이처럼
최종적으로 가장 중요한 것은 CORBA를
적용하려는 환경에서 요구하는 것이 무엇인가를 알아내는 것이다.
3장 CORBA 구축 방법론
이 장에서는 CORBA를
이용하여 시스템을 구축하는 절차 및 방법론에 대해 정리해 본다.
물론, 여기서
소개하는 절차 및 방법론이 절대적인 것은 아니다.
그러나 많은 사례를 분석하고
정리하면서 공통적인 사항들을 추려낸 것이기 때문에 CORBA
기반의 시스템을 구성하는 데
많은 도움을 줄 것이다.
3.1. CORBA를 이용한
전형적인 개발 과정 이해
단계1) 문제
정의 및 요구 분석
일단 가장 먼저 해야 할 작업은 구축하고자
하는 도메인의 현실을 직시하는 것이다. 이를
위해 현실 도메인에서 필요로 하는 요구 사항을 분석하고 문제를 정의한다.
이때 일반적인 것으로 요구 사항
명세와 사용 시나리오를 정리한다. 그리고
이들 요구 사항을 해결하는 시스템을 구축하기 위한 방법을 결정한다.
이러한 구축 방법을 결정하기 위해 먼저
CORBA를 적용한다는
생각에서 벗어나 현재의 공통적인 개발 환경인 네트웍 환경에서 이종의
하드웨어와 소프트웨어 환경에 무관한 복잡한 시스템을 구성하는 데
필요한 기본 프레임웍을 선택한다고 할 때 필요한 몇 가지 기준에
대해 생각해 보자.
먼저 , 이 프레임웍은 사용하기 쉬워야 한다.
짧은 기간에 쉽게 습득할 수 있고 간단한
사용법을 갖고 있어야 한다.
둘째 , 이 프레임웍은 유연해야 한다.
다양한 표준을 지원하고 서로 다른 응용
프로그램이나 모델에 상관없이 사용할 수 있어야 한다.
세째 , 이 프레임웍은 확장성이 있어야 한다.
적용하려는 분야에 필요한 기능을 구현하여
쉽게 확장할 수 있어야 하며 새로운 표준으로 쉽게 전이될 수 있어야
한다.
넷째, 이 프레임웍은 결합력이 있어야 한다.
WWW나 OLE,
OpenDoc같이 기존의 새로운 기술과
향후 출현하는 기술들과 쉽게 결합하여 사용할 수 있어야 한다.
이러한 기준에 의해서 우리는 기존의 DCE나
CORBA, OLE등
여러가지 프레임웍을 고려할 수 있다. 물론
우리는 당연히 CORBA를
사용하기로 결정할 것이다.
단계2) 분석과
디자인
일단 문제 정의가 끝났으면 작성된 요구
사항 명세와 사용 시나리오에 기초하여 분석과 디자인을 시작한다.
일반적으로 OMT같은
객체 지향 분석과 설계 기법을 사용한다. 실제
이들 방법에 대한 자세한 내용은 상당히 많은 시간과 노력을 투자해야
한다. 특히
이들 방법은 완전히 몸에 배일 때까지 사용하지 않으면 정확히 이해하기
힘들다. 하지만
이들 방법 중 우리는 먼저 해당 시스템에서 필요한 객체를 정의하고
이들 객체의 애트리뷰트와 메소드를 정의하며 객체들 사이의 연관성을
정의한다.
단계3) IDL정의
일단 분석과 디자인이 결정되면 이 디자인
명세에 따라 CORBA IDL 인터페이스를
작성한다.
단계4) IDL을
프로그램 언어로 컴파일
일단 작성된 CORBA
IDL파일은 원하는 형태의 개발 언어로
컴파일 할 수 있다. 참고로
다음은 현재 CORBA제품들에서
지원하는 언어를 정리한 것이다.
이들 지원 언어 중 현재 개발팀에서 사용하거나
사용할 언어로 컴파일을 한다. 만약
개발 후 다른 언어로 바꾼다고 해도 단지 해당 IDL
파일을 다시 컴파일 하면 된다.
단계5) 원하는 응용 프로그램 로직 추가
IDL 컴파일을 통해
생성된 파일을 바탕으로 원하는 객체의 메소드와 로직을 구현한다.
단계6) 해당 시스템들에 개발된 구성요소 배치
개발된 구현 객체들을 원하는 시스템 구성
방침에 따라 배치한다.
단계7) 시스템 구동
구축된 시스템을 가동한다.
실제 이 과정은 상당히 직선적이다.
이러한 직선적인 개발 방법을 "폭포식
개발"이라고 한다. 그러나
실제 개발 과정은 프로토타이핑이라 불리는 방법을 사용하는 것이
일반적이다. 프로토타이핑
방법은 각 개발 사이클을 여러 번 수행하면서 시스템을 개발,
안정화시키는 것이다.
따라서 위의 각 단계들은 필요에
따라 여러 번 되풀이 되거나 이전 단계로 되돌아 갈 수 있다.
3.2. 실제 구현
과정 예
다음은 기업에서 자금을 관리하는 가상의
시나리오를 통해 CORBA를
기반으로 한 시스템의 개발 과정을 추적해 본다.
단계1) 문제
정의 및 요구 분석
1) 문제 정의
구축하려는 목표 시스템은 기업의 자금관리에
필요한 모든 기능을 제공함을 목표로 한다.
2) 요구 사항 명세
3) 사용 시나리오
지금까지 설정한 요구 사항과 사용 시나리오를
바탕으로 한 전체 시스템의 물리적인 구성은 [그림7-1]과
같다. 원격지의
자금 담당자들의 PC와
중앙의 컴퓨터가 연결되어 있는 단순한 구조지만 현재 대부분의 은행이나
증권회사, 또는
철도 예매 등 다양한 곳에서 사용하고 있는 구조이다.

[그림7-1]
목표 시스템의 구성
자금 담당자는 서로 합의된 형태로 자금의 상태 정보를 파일에 저장한다.
이 파일은 파일 전송 프로토콜을 통해 전송된다.
전송된 파일은 데이타베이스에 갱신될 때까지 중앙 컴퓨터에 저장한다.
중앙 컴퓨터의 작업 프로그램은 이 파일을 읽고 해당 정보를 데이타베이스에 갱신한다. 이 작업 프로그램은 배치 방식으로 작업한다.
자금 관리자는 스프레드시트에 자금 현황을 기록해 두고 있다.
보고를 위해 비주얼 베이직으로 작성된 CORBA클라이언트를 수행한다.
이 클라이언트는 중앙의 컴퓨터에서 수행중인 CORBA 구현 객체 서버에게 서비스를 요청한다. 이때 스프레드시트에 정보가 함께 전달된다.
해당 CORBA서버는
직접 데이타베이스를 갱신한다.
이러한 방법들 외에도 여러 가지 대안이
존재할 수 있다. 그러나
앞서 언급한 여러 가지 이유에 의해서 우리는 CORBA를
이용하여 시스템을 구축하기로 한다.
단계2) 분석과
디자인
이 단계에서 우리는 원하는 객체들의 애트리뷰트와
메소드를 정의하고 만약 객체들 사이의 연관성이 필요하면 이들 사이의
관계를 설정한다. 가장
일반적으로 객체를 정의하는 방법은 이전 단계에서 얻은 문제 정의와
요구 사항 명세에서 명사로 된 것을 추리는 것이다.
자금 담당자 , 자금
, 정부 부처
, 데이타베이스
, 컴퓨터,
...
이것들이 바로 해당 시스템에서 필요로
하는 객체들이다. 실제
큰 프로젝트에서는 이들 객체를 바탕으로 객체 다이어그램이나 데이타
플로우 그림(DFD, Data Flow Diagram) 등
다양한 분석과 디자인 방법을 동원하여 구현하고자 하는 시스템을
설계한다. 실제
이 과정은 시스템 구축의 성공 여부를 가늠할 만큼 중요하다.
그러나 이 단계에서 필요한 것은
객체 지향 분석과 설계 방법 그 자체 기술과 CORBA를
비롯하여 여러 가지 기술과 구축 경험이 있는 사람들의 노하우이다.
본인의 견해로는 이 두 기능을
가진 엔지니어가 CORBA프로젝트의
리더로서 적합하다고 생각한다.
일단 분석과 디자인 과정을 통해 시스템
구축을 위한 CORBA 인터페이스로서
"자금"이란 객체와 특정 자금의 정보를 중앙의 데이타베이스에
갱신하기 위해 update란
메소드를 정의한다.
- 객체 이름 : fund
- 애트리뷰트 : fund_name
- 메소드 :
update( passwd , fund_size , bid_price , offer_price)
단계3) IDL정의
앞선 정의에 따라 CORBA
IDL을 다음과 같이 정의한다.
// fund.idl
intreface fund {
attribute string fund_name;
boolean update (
in string passwd,
in long fund_size,
in long bid_price,
in long offer_price ) ;
};
단계4) IDL을
프로그램 언어로 컴파일
일단 모든 IDL 파일이
작성되면 이젠 구현 언어가 등장할 차례이다.
다들 주지하다시피 IDL은
단순한 정의 언어이지 C나
C++같이 구현
언어가 아니다. 따라서
IDL 정의 파일을
C나 C++
또는 자바같은 구현 언어로 컴파일을
해야 한다.
다음과 같이 CORBA
IDL컴파일러를 사용하여 컴파일한다.
C:> idl fund.idl
컴파일 후 다음의 코드들이 생성된다.
실제 이 코드는 Orbix의
IDL 컴파일러가
생성해낸 파일들이다. Orbix Desktop 버전은
CORBA-OLE인터페이스를
위해 winidl이라는
별도의 컴파일러를 제공한다. 이
컴파일러는 해당 IDL파일을
COM의 ODL파일로
변환하는 등 OLE를
통해 CORBA객체를
이용할 수 있게 해준다.
이 파일은 fund 인터페이스의
클라이언트 객체가 구현 객체에 접근하게 해준다.
다시 말해 원격지에서 해당 서버
컴퓨터로의 네트웍 기능과 객체에 바인딩하는 기능 등 분산환경에서
객체를 조작하는 데 필요한 기본 기능들이 포함되어 있다.
이 파일은 fund.hh
라는 공통의 헤더 파일을 포함한다.
#include "fund.hh"
fund::fund (char *IT_OR) {
m_pptr = new fund_dispatch (IT_OR, this,(CORBA_Object*)this);
}
fund::fund (ObjectReference *IT_OR) {
m_pptr = new fund_dispatch (IT_OR, this,(CORBA_Object*)this);
}
#ifndef fundForwC
#define fundForwC
CORBA_ObjectRef fund_getBase(void *IT_p){
return (fund*) IT_p;}
void fund_release (fund_ptr IT_p, CORBA_Environment &IT_env) {
CORBA_release(IT_p, IT_env);}
void fund_release (fund_ptr IT_p) {
fund_release (IT_p, CORBA_IT_chooseDefaultEnv ()); }
fund_ptr fund_nil (CORBA_Environment &) {
return fund:: _nil ();}
fund_ptr fund_nil () {
return fund_nil (CORBA_IT_chooseDefaultEnv ());}
fund_ptr fund_duplicate (fund_ptr IT_p, CORBA_Environment &IT_env) {
return (fund::_duplicate(IT_p, IT_env)); }
fund_ptr fund_duplicate (fund_ptr IT_p) {
return fund_duplicate (IT_p, CORBA_IT_chooseDefaultEnv ()); }
#endif
fund_ptr fund::_duplicate(fund_ptr obj, CORBA_Environment& IT_pEnv) {
CORBA_EnvExcRaiser IT_raise;
CORBA_Environment &IT_env = IT_raise.registerEnv (&IT_pEnv);
if (!obj) {
return (obj);
}
fund_ptr IT_obj = obj->__duplicate (IT_env);
return IT_obj;
}
fund* fund:: _bind (const char* IT_markerServer, const char* host,
const CORBA_Context &IT_c,
CORBA_Environment &IT_env) {
return (fund*)CORBA_Factory.New (IT_markerServer, IT_env, IT_c, host,
fund_IMPL, fund_IR);
}
fund* fund:: _bind (CORBA_Environment &IT_env) {
return _bind (NULL,NULL,CORBA_Context(), IT_env); }
fund* fund:: _bind (const char* IT_markerServer, const char* host, CORBA_Environment &IT_env) {
return _bind (IT_markerServer, host, CORBA_Context (), IT_env); }
fund* fund::_narrow (CORBA_Object* IT_obj, CORBA_Environment &IT_pEnv) {
CORBA_EnvExcRaiser IT_raise;
CORBA_Environment &IT_env = IT_raise.registerEnv (&IT_pEnv);
if (CORBA_is_nil (IT_obj)) {
return fund:: _nil();
}
fund* IT_p = (fund*)CORBA_Object::_castDown (IT_obj, fund_IR, IT_env);
IT_p = IT_p ? IT_p->__duplicate(IT_env) : NULL;
if ((IT_env) &&(!strcmp(IT_env.exception()->id(), "CORBA::BAD_PARAM")) && (((CORBA_BAD_PARAM*)IT_env.exception())-> minor() == NARROW_FAILED)) {
IT_env.exception_free();
}
return IT_p;
}
void* fundProxyFactoryClass::New (char *IT_OR, CORBA_Environment&) {
return new class fund(IT_OR);}
void* fundProxyFactoryClass::New (ObjectReference *IT_OR, CORBA_Environment&) {
return new class fund(IT_OR);}
void* fundProxyFactoryClass::New2 () {return new class fund();}
void fundProxyFactoryClass::Delete (void * o) {if(o) delete (CORBA_Object *) o; }
void* fundProxyFactoryClass::IT_castUp (void *IT_p, char* IT_s, CORBA_Environment &IT_env) {
void *IT_l;
if (!CORBA__interfaceCmp (IT_s,fund_IR, IT_env))
return IT_p;
else if (IT_l=CORBA_ObjectFactoryClass::IT_castUp((CORBA_Object*) (( class fund* ) IT_p) , IT_s, IT_env))
return IT_l;
else return NULL;
}
CORBA_PPTR* fundProxyFactoryClass::pptr (void *IT_p) {
return ((class fund*)IT_p)->_pptr ();}
void fundProxyFactoryClass::baseInterfaces (_IDL_SEQUENCE_string& seq) {
add (seq, fund_IR);
CORBA_ObjectFactoryClass::baseInterfaces (seq);
}
void fund::fund_name (const char * fund_name, CORBA_Environment
&IT_pEnv) {
CORBA_EnvExcRaiser IT_raise;
CORBA_Environment &IT_env = IT_raise.registerEnv (&IT_pEnv);
if (IT_env || m_isNull) {return;}
CORBA_Request IT_r (this, "_set_fund_name",IT_env,1,0);
if (!IT_r.isException (IT_env)) { IT_r.encodeStringOp (fund_name); }
IT_r.invoke(CORBA_Flags(0), IT_env);
if (IT_r.exceptionKind () == SYSTEM_EXCEPTION) {
IT_r.checkEnv (IT_env);
}
}
char * fund::fund_name (CORBA_Environment &IT_pEnv) {
CORBA_EnvExcRaiser IT_raise;
CORBA_Environment &IT_env = IT_raise.registerEnv (&IT_pEnv);
if (IT_env || m_isNull) {
return ((char *) NULL);
}
CORBA_Request IT_r (this, "_get_fund_name",IT_env,1,0);
IT_r.invoke(CORBA_Flags(0), IT_env);
if (!IT_r.isException (IT_env)) {
char * fund_name;
IT_r.decodeStringOp(fund_name);
IT_r.checkEnv (IT_env);
return fund_name;
}
if (IT_r.exceptionKind () == SYSTEM_EXCEPTION) {
IT_r.checkEnv (IT_env);
}
return ((char *) NULL);
}
CORBA_Boolean fund:: update (const char * passwd, CORBA_Long fund_size, CORBA_Long bid_price, CORBA_Long offer_price, CORBA_Environment &IT_pEnv) {
CORBA_EnvExcRaiser IT_raise;
CORBA_Environment &IT_env = IT_raise.registerEnv (&IT_pEnv);
if (IT_env || m_isNull) {
return 0;
}
CORBA_Request IT_r (this, "update",IT_env,1,0);
if (!IT_r.isException (IT_env)) {
IT_r.encodeStringOp (passwd);
IT_r << fund_size; IT_r << bid_price;
IT_r << offer_price;
}
IT_r.invoke (CORBA_Flags(0),IT_env);
if (!IT_r.isException (IT_env)) {
CORBA_Boolean IT_result;
IT_r >> IT_result;
IT_r.checkEnv (IT_env);
return IT_result;
}
if (IT_r.exceptionKind () == SYSTEM_EXCEPTION) {
IT_r.checkEnv (IT_env);
}
return 0;
}
fundProxyFactoryClass fundProxyFactory(1);
#ifndef fund_dispatch_impl
unsigned char fund_dispatch::dispatch (CORBA_Request &IT_r,
unsigned char, void *) {
IT_r.makeRuntimeException1 ("fund");
return 0;
}
#endif
이 파일에는 구현 객체에 필요한 각종 기능이
포함되어 있다. 이
파일 역시 fund.hh 라는
공통의 헤더 파일을 포함한다.
#include "fund.hh"
#define fund_dispatch_impl
unsigned char fund_dispatch::dispatch (CORBA_Request &IT_r,
unsigned char IT_isTarget, void *IT_pp) {
if (!IT_pp) IT_pp = m_obj;
const char *IT_s = IT_r.getOperation ();
if (!strcmp (IT_s,"_get_fund_name")) {
char * fund_name;
CORBA_Environment IT_env (IT_r);
CORBA_Filter* IT_f = CORBA_Orbix.getFilter ();
if (!IT_r.tcAssert ("\Ro~_get_fund_name~>{0},N{}\"))
return 1;
if (IT_f)
IT_f->inRequestPostM (IT_r, IT_env);
if (!IT_r.isException (IT_env)) {
try {
fund_name = ((fund*)IT_pp)->fund_name(IT_env);
} catch (CORBA_SystemException &IT_exSys) {
IT_env.propagate (&IT_exSys);
} catch (CORBA_UserException &IT_exUsr) {
IT_env.propagate (&IT_exUsr);
}
}
if (!IT_r.isException (IT_env)) {
if (!IT_r.convertToReply ("\0\", IT_env)) return 1;
IT_r.encodeStringOp (fund_name);
CORBA_string_free (fund_name);
}
else IT_r.makeSystemException (IT_env);
return 1;
}
else if (!strcmp (IT_s,"_set_fund_name")) {
CORBA_Environment IT_env (IT_r);
CORBA_Filter* IT_f = CORBA_Orbix.getFilter ();
if (IT_r.tcAssert ("\Ro~_set_fund_name~+{0},>{v},N{}\")) {
char * fund_name;
IT_r.decodeStringOp(fund_name);
if (IT_f && !IT_r.isException (IT_env))
IT_f->inRequestPostM (IT_r, IT_env);
if (!IT_r.isException (IT_env)) {
try {
((fund*)IT_pp)->fund_name(fund_name, IT_env);
} catch (CORBA_SystemException &IT_exSys) {
IT_env.propagate (&IT_exSys);
} catch (CORBA_UserException &IT_exUsr) {
IT_env.propagate (&IT_exUsr);
}
}
CORBA_string_free (fund_name);
}
IT_r.replyNoResults (IT_env);
return 1;
}
else if (!strcmp(IT_s,"update")) {
CORBA_Boolean IT_result;
CORBA_Environment IT_env (IT_r);
CORBA_Filter* IT_f = CORBA_Orbix.getFilter ();
if (!IT_r.tcAssert ("\
Ro~update~+passwd{0},+fund_size{l},+bid_price{l},+offer_price{l},>{b},N{}\
"))
return 1;
char * passwd;
IT_r.decodeStringOp(passwd);
CORBA_Long fund_size;
IT_r >> fund_size;
CORBA_Long bid_price;
IT_r >> bid_price;
CORBA_Long offer_price;
IT_r >> offer_price;
if (IT_f && !IT_r.isException (IT_env))
IT_f->inRequestPostM (IT_r, IT_env);
if (!IT_r.isException (IT_env)) {
try {
IT_result = ((fund*)IT_pp)->update
( passwd, fund_size, bid_price, offer_price, IT_env);
} catch (CORBA_SystemException &IT_exSys) {
IT_env.propagate (&IT_exSys);
} catch (CORBA_UserException &IT_exUsr) {
IT_env.propagate (&IT_exUsr);
}
}
CORBA_string_free (passwd);
if (!IT_r.isException (IT_env)) {
if (!IT_r.convertToReply ("\b\", IT_env)) return 1;
IT_r << IT_result;
}
else IT_r.makeSystemException (IT_env);
return 1;
}
else if (IT_isTarget)
IT_r.makeRuntimeException2 ();
return 0;
}
#include "fundC.CPP"
이 헤더 파일에는 fund
객체를 네트웍 상에서 사용하기
위한 각종 클래스 정의가 포함되어 있다. 먼저
각종 CORBA 기능을
지원하기 위해 미리 시스템 클래스 차원에서 정의되어 있는 CORBA.h라는
헤더파일을 삽입한다. 그리고
기본적으로 CORBA의
기본 객체아답타(BOA, Basic Object Adapter)를
사용하기 위해 BOAImple이라는
클래스를 상속받은 fund객체와
실행중에 객체를 이용할 수 있도록 하기위해 TIE라는
객체를 상속받은 fund객체를
선언하고 있다. 이
외에도 fund객체의
객체참조자 조작을 위한 정의 등이 포함되어 있다.
#ifndef fund_hh
#define fund_hh
#include <CORBA.h>
#include <string.h>
#ifndef _fund_defined
#define _fund_defined
class fund_dispatch : public virtual CORBA_PPTR {
public:
fund_dispatch (void *IT_p, CORBA_Object* IT_o, const char *IT_m,
CORBA_LoaderClass *IT_l, char *IT_i, void* IT_im)
: CORBA_PPTR (IT_p,IT_o,IT_m,IT_l,IT_i,IT_im) {}
fund_dispatch (char *IT_OR, void *IT_p, CORBA_Object *IT_o)
: CORBA_PPTR (IT_OR,IT_p,IT_o) {}
fund_dispatch () {}
fund_dispatch (ObjectReference *IT_OR, void *IT_p, CORBA_Object *IT_o)
: CORBA_PPTR (IT_OR,IT_p,IT_o) {}
fund_dispatch (void *IT_p, CORBA_Object *IT_o, const char *IT_m,
char *IT_i, CORBA_Object* IT_ob, void* IT_im)
: CORBA_PPTR (IT_p,IT_o,IT_m,IT_i,IT_ob,IT_im) {}
virtual unsigned char dispatch (CORBA_Request &IT_r,
unsigned char IT_isTarget, void* IT_pp=NULL);
};
class fund;
#ifndef fundPtr
#define fundPtr
typedef fund* fund_ptr;
typedef fund* fundRef;
#endif
#ifndef fundForwH
#define fundForwH
CORBA_ObjectRef fund_getBase (void *);
void fund_release (fund *, CORBA_Environment &IT_env);
void fund_release (fund_ptr);
fund* fund_duplicate (fund_ptr, CORBA_Environment &IT_env);
fund* fund_duplicate (fund_ptr );
fund_ptr fund_nil (CORBA_Environment &IT_env);
fund_ptr fund_nil ();
#endif
#define fund_IMPL "fund"
class fund;
typedef fund fundProxy;
#define fund_IR "fund"
#define fund_IMPL "fund"
#ifndef fundPtr
#define fundPtr
typedef fund* fund_ptr;
typedef fund* fundRef;
#endif
class fund: public virtual CORBA_Object {
public:
fund (char *IT_OR);
fund (ObjectReference *IT_OR);
fund () : CORBA_Object (1) {}
protected:
fund_ptr __duplicate(
CORBA_Environment &IT_env=CORBA_IT_chooseDefaultEnv ()) {
CORBA_Object::__duplicate (IT_env);
return this;
}
public:
static fund_ptr _duplicate(
fund_ptr,
CORBA_Environment &IT_pEnv=CORBA_IT_chooseDefaultEnv ());
public:
static fund* _bind (const char* IT_markerServer, const char* host,
const CORBA_Context &IT_c,
CORBA_Environment &IT_env=CORBA_IT_chooseDefaultEnv ());
static fund* _bind (CORBA_Environment &IT_env);
static fund* _bind (const char* IT_markerServer=NULL, const char* host=NULL,
CORBA_Environment &IT_env=CORBA_IT_chooseDefaultEnv ());
static fund* _narrow (CORBA_Object* , CORBA_Environment &IT_env=CORBA_IT_chooseDefaultEnv ());
static fund_ptr _nil (CORBA_Environment &IT_pEnv=CORBA_IT_chooseDefaultEnv ()) {
CORBA_EnvExcRaiser IT_raise;
CORBA_Environment &IT_env = IT_raise.registerEnv (&IT_pEnv);
return (fund_ptr) CORBA_OBJECT_NIL;}
virtual void fund_name (const char * fund_name, CORBA_Environment &IT_env=CORBA_IT_chooseDefaultEnv ()) ;
virtual char * fund_name (CORBA_Environment &IT_env=CORBA_IT_chooseDefaultEnv ()) ;
virtual CORBA_Boolean update (const char * passwd, CORBA_Long fund_size, CORBA_Long bid_price, CORBA_Long offer_price, CORBA_Environment &IT_env=CORBA_IT_chooseDefaultEnv ()) ;
};
#ifndef fundVarH
#define fundVarH
#ifndef fundvPtr
#define fundvPtr
typedef fund* fund_vPtr;
typedef const fund* fund_cvPtr;
#endif
class fund_var : public CORBA__var
{
private:
unsigned char copyHelper (const fund_var &IT_s) {
{ _ptr = fund_duplicate (IT_s._ptr);
}
return 1;
}
public:
fund_var (const fund_var &IT_s) {(void) copyHelper (IT_s); }
fund_var () {_ptr = fund_nil (); }
fund_var (fund *IT_p) { _ptr = IT_p; }
fund_var &operator= (fund *IT_p) {
fund_release (_ptr);
_ptr = IT_p;
return (*this);
}
fund_var &operator= (const fund_var &IT_s) {
fund_release (_ptr);
_ptr = fund_duplicate (IT_s._ptr);
return (*this);
}
~fund_var () { fund_release (_ptr); }
fund* operator-> () {return _ptr; }
operator const fund_vPtr () const { return _ptr;}
operator fund_vPtr& () { return _ptr;}
protected:
fund *_ptr;
private:
fund_var &operator= (const CORBA__var &IT_s);
fund_var (const CORBA__var &IT_s);
fund_var &operator= (const CORBA__mgr &IT_s);
fund_var &operator= (const CORBA__SeqElem &IT_s);
fund_var (const CORBA__mgr &IT_s);
fund_var (const CORBA__SeqElem &IT_s);
};
#endif
#ifndef fundMgrH
#define fundMgrH
class fund_mgr : public CORBA__mgr
{
public:
fund_mgr () {
_ptr = fund_nil ();
_release = 1;
}
fund_mgr (const fund_mgr &IT_s) {
_ptr = fund_duplicate (IT_s._ptr);
_release = 1;
}
fund_mgr &operator= (fund *IT_p) {
if (_ptr && _release)
fund_release (_ptr);
_ptr = IT_p;
_release = 1;
return (*this);
}
fund_mgr &operator= (const fund_mgr &IT_s) {
if (_ptr && _release)
fund_release (_ptr);
_ptr = fund_duplicate(IT_s._ptr);
_release = 1;
return (*this);
}
fund_mgr &operator= (fund_var &IT_s) {
if (_ptr && _release)
fund_release (_ptr);
_ptr = fund_duplicate(IT_s);
_release = 1;
return (*this);
}
~fund_mgr () {
if (_release)
fund_release (_ptr);
}
unsigned char release () {
return _release;
}
void release (unsigned char rel) {
_release = rel;
}
operator int () const {
return (((CORBA_Object_ptr) _ptr) ? 1 : 0);
}
operator void* () const {
return _ptr;
}
operator CORBA_Object * () const {
return (CORBA_Object *) _ptr;
}
operator fund* () const { return (fund*) _ptr;}
fund *_ptr;
protected:
unsigned char _release;
};
#endif
#ifndef fundSeqElemH
#define fundSeqElemH
class fund_SeqElem : public CORBA__SeqElem
{
public:
fund_SeqElem (fund_ptr* IT_p, unsigned char rel) {
_ptr = IT_p;
_release = rel;
}
fund_SeqElem &operator= (fund_ptr IT_p) {
if (_ptr) {
if (*(_ptr) && _release)
fund_release (*(_ptr));
*(_ptr) = IT_p;
}
return (*this);
}
fund_SeqElem &operator= (const fund_SeqElem &IT_s) {
if (_ptr && IT_s._ptr) {
if (*(_ptr) && _release)
fund_release (*(_ptr));
*(_ptr) = fund_duplicate(*(IT_s._ptr));
}
return (*this);
}
operator fund_ptr () const {
return _ptr ? (fund_ptr) (*_ptr) : fund_nil();
}
fund_ptr operator->() const { return *_ptr;}
protected:
fund_ptr *_ptr;
unsigned char _release;
};
#endif
#define TIE_fund(X) fund##X
#define DEF_TIE_fund(X) \
class fund##X : public virtual fund {\
X* m_obj;\
public:\
fund##X (X *objp, const char* m="", CORBA_LoaderClass *l=0)\
: fund(), CORBA_Object (), m_obj(objp) {\
m_pptr = new fund_dispatch \
(( fund*)this,(CORBA_Object*)this,m,l,fund_IR,m_obj);\
}\
fund##X (CORBA_Object *IT_p, const char* IT_m="", void *IT_q=0)\
: fund(), CORBA_Object () { \
m_pptr = new fund_dispatch \
(( fund*)this,(CORBA_Object*)this,IT_m,fund_IR,IT_p,IT_q); \
m_obj = (X*)(m_pptr->getImplObj ()); \
}\
virtual ~fund##X () {\
if (_okToDeleteImpl ()) delete m_obj; } \
virtual void* _deref () {\
return m_obj; }\
virtual void fund_name (const char * fund_name, CORBA_Environment &IT_env) {\
m_obj->fund_name(fund_name,IT_env); }\
virtual char * fund_name (CORBA_Environment &IT_env) {\
return m_obj->fund_name(IT_env); }\
virtual CORBA_Boolean update (const char * passwd, CORBA_Long fund_size,
CORBA_Long bid_price, CORBA_Long offer_price, CORBA_Environment &IT_env) {\
return m_obj->update ( passwd, fund_size, bid_price, offer_price,IT_env);\
}\
};
#define QUALS_fund \
virtual void fund_name (const char * fund_name, CORBA_Environment &IT_env) {\
m_obj->fund_name(fund_name,IT_env); }\
virtual char * fund_name (CORBA_Environment &IT_env) {\
return m_obj->fund_name(IT_env); }\
virtual CORBA_Boolean update (const char * passwd, CORBA_Long fund_size, CORBA_Long bid_price, CORBA_Long offer_price, CORBA_Environment &IT_env) {\
return m_obj->update ( passwd, fund_size, bid_price, offer_price,IT_env);\
}\
class fundProxyFactoryClass : public virtual CORBA_ObjectFactoryClass {
public:
fundProxyFactoryClass (unsigned char IT_p=0)
: CORBA_ProxyFactory (fund_IR, IT_p) {}
virtual void* New (char *IT_OR, CORBA_Environment&);
virtual void* New (ObjectReference *IT_OR, CORBA_Environment&);
virtual void* New2 ();
virtual void Delete (void *);
virtual void* IT_castUp (void *IT_p, char* IT_s, CORBA_Environment &IT_env=CORBA_IT_chooseDefaultEnv ());
virtual CORBA_PPTR* pptr (void *IT_p);
virtual void baseInterfaces (_IDL_SEQUENCE_string&);
};
extern fundProxyFactoryClass fundProxyFactory;
#endif
#endif
이 파일은 구현 객체의 헤더 파일이다.
#ifndef fund_ih
#define fund_ih
#include "fund.hh"
class fund== {
public:
virtual void fund_name (const char * fund_name, CORBA_Environment &IT_env=CORBA_default_environment) ;
virtual char * fund_name (CORBA_Environment &IT_env=CORBA_default_environment) ;
virtual CORBA_Boolean update (const char * passwd, CORBA_Long fund_size, CORBA_Long bid_price,
CORBA_Long offer_price, CORBA_Environment &IT_env=CORBA_default_environment) ;
};
#endif
이 파일에는 실제 구현 객체의 코드가 위치한다.
실제 여기서 제공되는 것은 작성해야
할 템플릿이다. 예를
들면, 여러분은
이 템플릿의 update()에
RDBMS를 조작하거나
ODBMS를 조작하는
등 원하는 코드를 작성해 넣어야 한다.
#include "fund.ih"
void fund==:: fund_name (const char * fund_name, CORBA_Environment &IT_env) {
// 원하는 코드를 추가한다.
}
char * fund==:: fund_name (CORBA_Environment &IT_env) {
// 원하는 코드를 추가한다.
}
CORBA_Boolean fund==:: update (const char * passwd, CORBA_Long fund_size, CORBA_Long bid_price, CORBA_Long offer_price, CORBA_Environment &IT_env) {
// 원하는 코드를 추가한다.
}
단계5) 원하는
응용 프로그램 로직 추가
일단 클라이언트와 구현 객체사이의 CORBA
통신 기능을 제공하는 파일들을
생성한 다음의 작업은 프로그래머의 몫이다.
다음 [그림7-2]는
CORBA 프로그래머에게
무슨 추가 코드가 필요한지 보여준다. CORBA
fund 구현 서버 객체는 중앙의 컴퓨터에
위치한다. 다이얼
업으로 연결되어 있는 자금 담당자들은 그들의 PC에서
fund 구현
서버 객체의 update 메소드를
호출함으로서 원하는 작업을 수행할 수 있다.
[ 그림7-2
] 목표 시스템에서의 CORBA객체
먼저 클라이언트에 추가적으로 필요한 코드
부분을 생각해보자.
- CORBA-OLE기능을
이용하여 비주얼 베이직이나 기타 프론트 개발도구를 통해 서비스를
직접 호출할 수 있는 사용자 인터페이스를 개발한다.
- 클라이언트는 _bind()
메소드를 통해 fund
구현 객체에 바인딩한다.
이때 fund
구현 객체의 이름은 fundUpdate이다.
- 구현 객체에 바인딩한
후 update() 메소드를
호출하여 해당 자금의 내용을 갱신한다.
이러한 클라이언트는 구현 객체의 내용이
바뀌더라도 메소드 호출시 인터페이스만 변하지 않는다면 전혀 문제가
없다. 또한
서버에 새로운 메소드가 추가되면 클라이언트는 이 메소드를 단순히
호출함으로서 이용할 수 있다.
클라이언트의 코드는 주로 사용자 인터페이스와
구현 객체의 메소드를 호출하는 코드이다. 그러나
서버에는 이 메소드의 실제 수행 코드가 구현된다.
- 서버 코드에서 fund_i는
fund 인터페이스의
구현 객체이다.
- fund_i 구현 객체는
중앙의 컴퓨터에서 수행되어 클라이언트의 모든 update()
메소드 호출을 수행한다.
fund_i 구현 객체는 CORBA
수행 모드에 따라 update()
메소드 호출때마다 수행되거나 하나의
구현 객체를 공유하거나 CORBA에
의해서가 아니라 관리자가 직접 수행시키는 등 다양하게 관리될 수
있다.
- fund_i객체의 update 메소드는 다음의 기능을 수행한다.
: 먼저 passwd를 검사한다.
: passwd가 정당하면
: passwd가 정당하지
않으면 false값을
반환하다.
모든 프로그램의 코드가 작성되면 해당
코드를 컴파일하여 클라이언트와 서버 바이너리를 작성한다.
단계6) 해당
시스템들에 개발된 구성요소 배치
개발된 클라이언트 바이너리는 자금 담당자의
PC에 설치하고
서버는 중앙의 컴퓨터에 설치한다. 이때
중요한 점은 중앙의 컴퓨터에 ORB 데몬이
위치해야 한다는 점이다. 물론
데몬 구조 형태가 아니라 DAIS처럼
클라이언트와 서버 라이브러리 형태로 구성되어 데몬이 필요없는 CORBA제품도
있다. 또한
구현 객체를 CORBA 인터페이스
저장소에 등록해야 한다.
만약 자금 담당자의 수가 증가하여 중앙의
컴퓨터에 있는 서버하나로 처리가 힘들 경우 기존의 중앙 컴퓨터에
fund 구현
객체를 추가하거나 새로운 서버 컴퓨터에 fund
구현 객체를 추가함으로서 간단하게
처리능력을 확장할 수 있다. 후자의
경우 두개의 중앙 컴퓨터가 하나의 데이타베이스를 공유한다.
이처럼 개발된 구성요소를 원하는
시스템에 배치한다.
단계7) 시스템
구동
훌륭하게 시스템이 사용되는 지 구동시켜
본다. 문제가
발생하면 당연히 구현 코드 부분을 수정한다.
7막을 마치며
CORBA는 컴포넌트
기반의 시스템을 개발하거나 기존의 시스템에 기반한 새로운 시스템을
구축할 때 사용된다. 따라서
CORBA를 사용할
때 어떤 제품을 사용할 것인가는 중요한 결정 사항이다.
일반적으로 CORBA
제품을 선택할 경우 2가지
고려 사항이 존재한다: CORBA 표준을
얼마나 준수하는가? 와
현재의 구축 환경에서 필요한 것이 무엇인가?
.
일단 CORBA제품이
선정되면 이를 이용하여 시스템을 구축한다.
이 때, 일반적으로
객체지향 분석과 설계, 그리고
프로토타이핑이라는 방법을 사용하여 빠르고 반복적으로 시스템을
개발한다.