오늘은 다른 사람이 만든 커맨드를 수행하되, 워크밴치 셀렉션을 직접 바꾸지 않고도 개발자가 원하는 셀렉션 모델에 대해 그 비즈니스 로직이 수행되게 하는 방법을 배워 보겠습니다.
커맨드의 수행 과정
CCI -> 핸들러 서비스: 커맨드 제출 핸들러 서비스 -> 평가 서비스: 컨텍스트 요청 평가 서비스 -> 핸들러 서비스: 컨텍스트 공급 핸들러 서비스 -> 핸들러 서비스: 핸들러 결정 핸들러 서비스 -> 핸들러: 실행 이벤트 핸들러 -> 핸들러 : 비즈니스 로직 수행
CCI (커맨드 컨트리뷰션 아이템)
JFace 를 공부하신 분이라면 ActionContributionItem(ACI) 을 기억 하실 겁니다. ACI가 액션을 툴바, 메뉴, 컴포지트등에 표현하고 액션의 상태가 변경될 때마다 표현한 UI를 업데이트하며, 사용자가 UI를 누르면 Action 을 수행하게 하는 임무를 갖고 있는 것 처럼, CCI는 커맨드를 UI에 기여하고 업데이트 시키며, UI가 눌렸을 때 커맨드를 수행합니다.
단, 액션은 비즈니스 로직을 직접 가진 반면, 커맨드는 스트링일 뿐입니다.
커맨드가 커맨드 플랫폼에 제출 되면, 핸들러 서비스는 현재 상황 (IEvaluationContext)에 따라 적절한 핸들러를 선택하고, 커맨드를 처리하게 합니다.
핸들러들은 확장을 통해 기술 되는데 특정 뷰가 활성 상태일 때, 또는 워크벤치의 선택 모델이 특정 타입일 때와 같은 특정 문맥(context)에서 활성화 되고 enable 되어야 한다는 선언(코어 익스프레션)도 함께 가지고 있습니다. 핸들러가 결정되면 핸들러의 execute(ExecutionEvent)가 호출되고, 이 안에 비즈니스 로직이 기술됩니다.
CCI들은 이러한 문맥에 따라 가용한 핸들러가 없는 경우, 기여한 UI를 disable 시키는 등의 작업도 수행합니다. 이렇게 커맨드가 화면상에 표현된 것들을 UIElement 라고 부릅니다.
핸들러
핸들러는 실행 될 때 ExecutionEvent를 공급 받습니다. 이 안에는 어플리케이션의 현재 문맥 상태, 파라미터 값과 같은 여러 정보가 들어 있으며 핸들러의 구현자는 HanlderUtil을 이용하여 이 정보를 보다 쉽게 억세스 할 수 있습니다.
우리의 목표는 핸들러의 비즈니스 로직을 이용하되, 핸들러가 워크벤치 셀렉션을 처리하는 것이 아닌 우리가 원하는 모델을 처리해 주길 원하는 것입니다. 그러나 핸들러와 커맨드는 우리가 만든 것이 아니므로, 내부 구현을 바꾸지 않고 이를 달성할 가장 좋은 방법은 핸들러의 입력 모델인 Execution Event 를 조작하여 공급하는 것입니다.
코드
IHandlerService handlerService = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class); ParameterizedCommandFactory factory = new ParameterizedCommandFactory("someCommandId"); ParameterizedCommand command = factory.buildCommand(); IEvaluationService evaluationService = (IEvaluationService) PlatformUI.getWorkbench().getService(IEvaluationService.class); IEvaluationContext state = evaluationService.getCurrentState(); StructuredSelection selection = // 셀렉션을 만듬 state.addVariable("selection", selection); handlerService.executeCommandInContext(command, null, state);
4~7 라인은 수행할 커맨드 ID를 이용하여 커맨드를 만드는 과정입니다.
ParameterizedCommandFactory는 파라미터 커맨드를 쉽게 만들 수 있게 해주는 유틸리티로, 이클립스의 기본 요소가 아닙니다. 여기에서 다운로드 받을 수 있습니다.
9~15 라인은 해당 커맨드가 수행될 환경을 조작하는 작업입니다.
우선 평가 서비스로 부터 현재 상태를 얻어 selecton의 값을 우리가 원하는 형태로 고쳐 씁니다. 만약 핸들러가 <with />익스 프레션으로 다른 변수를 접근하여 enableWhen이나 activeWhen에 사용한다면 그런 변수도 여기서 줄 수 있습니다.
17 라인에서는 조작된 환경을 바탕으로 커맨드를 제출 하여 수행하게 합니다. 두번째 인자가 null인 이유는 원래 이 자리엔 최초 이 일이 발생시킨 원인인 UI Event를 넣는 자리인데, 이 경우 프로그래밍으로 커맨드를 수행하는 것이므로, 이벤트가 따로 존재하지 않습니다.
의미와 응용
Action 개체는 UI와 비즈니스 로직의 결합도가 굉장히 심합니다. ACI계층이 있어도 이 ACI는 Action으로 부터 텍스트, 레이블, 아이콘등을 가져오기 때문에, 초기 화면을 구성할 때 비즈니스 로직이 담긴 Action 클래스들을 모두 로드해야 합니다. 반면 Action은 run 을 직접 호출함으로써 개발자는 손 쉽게 비즈니스 로직만 리유즈가 가능하며, 해당 액션의 필드를 변경하여 동작을 수정해가며 재사용 가능합니다. (물론 이 Action 클래스에 대한 가시성이 있다는 전제하에)
그러나 핸들러는 기본적으로 비공개성향 (대부분 인터널 패키지)이 강하고 커맨드 ID만 외부에 노출되어 입맛에 맞게 그 비즈니스 로직을 리유즈 하는 것이 난해합니다. 이 포스트에서 익힌 테크닉을 이용하면 네비게이터에서 숨겨져 보이지 않는 파일을 선택하고 삭제 명령을 수행한다던가, GUI테스트 시에 목업으로 사용한다던가, 특정 핸들러가 다른 핸들러들을 순차적으로 이용하여 데이터를 가공해 나간다던가 하는 식의 응용이 가능할 것입니다.
'PDE' 카테고리의 다른 글
빌드 자동화 하기 #1 - Hello Ant (2) | 2011.03.07 |
---|---|
이클립스 플러그인에 DLL 포함시키기 (0) | 2010.12.10 |
제품 빌드시 자주 발생하는 문제들 (0) | 2010.11.26 |
Bundle과 Resource (0) | 2010.10.28 |
네이쳐와 빌더 (0) | 2010.10.15 |