오늘은 다른 사람이 만든 커맨드를 수행하되, 워크밴치 셀렉션을 직접 바꾸지 않고도 개발자가 원하는 셀렉션 모델에 대해 그 비즈니스 로직이 수행되게 하는 방법을 배워 보겠습니다.

커맨드의 수행 과정

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
Posted by 지이이이율
,

이 씨리즈의 문서는 이클립스 피쳐 프로젝트를 자동 빌드하고, 서명, 업데이트 사이트 및 리포지터리를 자동으로 업데이트 하는 방법을 익히게 하는 것을 목적으로 합니다. 초보적인 Ant이해를 가진 이클립스 플러그인 개발자를 대상으로 합니다.

ANT 파일 만들기

우선 빌드를 위한 빈 자바 프로젝트를 HelloAntBuild라는 이름으로 만듭니다. 자바 프로젝트로 만드는 이유는, Ant스크립트로 처리할 수 없는 일괄작업을 Java로 기술해야만 하는 경우를 대비하기 위해서 입니다.

그리고 HelloAnt.build.xml 라는 파일을 만들고 다음의 내용을 입력합니다:



	 
		Hello Ant!
	

XML 또는 텍스트 에디터를 닫고, 이 파일을 Ant Editor로 엽니다. 아웃라인 뷰에서, build를 우클릭한 뒤, [Run As] / [Ant Build] 를 차례로 선택하여, 화면에 Hello Ant!가 잘 찍히는 지 확인합니다.

수행결과

Eclipse Ant Task 사용하기

이클립스에서는 Ant에서 사용할 수 있는 유용한 태스크 태그(동작)와 프로퍼티(변수)들을 공급합니다. 단, 이를 사용하기 위해서는 앤트가 실행중인 이클립스와 동일한 JVM에서 수행되어야 합니다. 아웃라인 뷰에서 build를 우클릭한 뒤, [Run As] / [Ant Build ...] 을 선택하여, 앤트 실행 구성 편집기를 엽니다.

JRE 탭에서 Run in the Same JRE as the workspace를 선택합니다.

이 앤트 파일의 실행 구성을 팀원들과 공유하기 위하여, Common 탭 Save as 항목에서 실행 구성이 저장될 위치 (워크 스페이스 내 현재 프로젝트)를 선택해 줍니다. 앞으로 이 프로젝트를 체크아웃 받아 빌드를 실행하는 다른 사람도, 자동으로 이 실행 구성을 이용하게 됩니다.

이제 Run 또는 Debug 버튼을 눌러 앤트를 실행시켜보면, 이전과 마찬가지 실행결과를 보여주지만, 네비게이터를 살펴보면 프로젝트 안에 "프로젝트이름 HeloAnt.build.xml.launch" 라는 파일이 하나 생겨 있을 것입니다. 이 파일이 앤트 실행 구성을 담고 있습니다.

이클립스 프로퍼티 사용해 보기

앤트 파일을 다시 열고, 다음의 내용을 추가합니다:


	현재 실행중인 이클립스의 위치는 ${eclipse.home.location} 입니다.

이제 이 타깃을 실행 해 봅시다. 현재 실행중인 이클립스의 위치가 보인다면 잘 따라오고 계신겁니다 :)

플러그인 빌드 하기

하나의 플러그인이 빌드 되는 과정은 매우 단순합니다.

  1. 플러그인 디펜던시에 있는 모든 플러그인과, 그 플러그인들의 디펜던시를 모두 얻어 클래스 패스에 추가한 상태로, 모든 자바 파일을 컴파일 한다
  2. build.properties 및 manifest.mf 파일의 내용에 따라, 컴파일 결과물 및 리소스를 jar파일에 집어 넣는다.
  3. 필요한 경우 완성된 jar 파일을 서명

말은 쉽지만, 1번 단계부터 막막할 겁니다.

build.properties와, manifest.mf파일을 자동으로 분석해서 해야할 일을 자동으로 생성해 주는 녀석이 있다면 아주 편리하겠죠? 다행히 eclipse.buildScript 라는 태스크가 그 일을 지원합니다. 우리는 이 태스크 태그를 이용하여, 플러그인을 빌드하는 스크립트를 생성 한 뒤에 실행 할 것입니다. 이 전략을 그림으로 그려보면 다음과 같습니다:

Main Script -> Sub Script* : generate
Sub Script* -> Sub Script* : run
Main Script -> Sub Script* : delete

연습용 플러그인 만들기

이제 자동빌드를 연습해볼 마루타 프로젝트인 HelloAntPlugin 플러그인 프로젝트를 하나 만듭니다. 특별히 아무런 구현이나 내용이 없어도 됩니다.

플러그인 프로젝트를 만들었으면, 빌드 프로젝트의 HelloAnt.build.xml을 다시 열어 build-plugin-test라는 타깃을 앤트파일에 추가하고 아래의 내용을 채워 넣으세요:


	<eclipse.buildScript 
		elements="plugin@HelloAntPlugin" 
		baselocation="${eclipse.home}" 
		builddirectory="${basedir}/../" />
	<eclipse.refreshLocal resource="/" depth="infinite" />

eclipse.buildscript 태스크는 다음과 같은 속성을 가집니다:

  • elements : 앤트 빌드 스크립트를 생성할 대상. 타입@ID 형태로 지정합니다. 우리가 빌드할 것은 플러그인이고, 아이디는 HelloAntPlugin이므로 plugin@HelloAntPlugin이 됩니다.
  • baselocation : 대상을 빌드하는데 필요한 디펜던시를 어디에서 부터 얻을지 알려줍니다. 설치된 이클립스의 경로를 알려줬습니다. 개발 타겟이 다른경우, 이곳에 타겟 이클립스의 경로를 지정하면 됩니다.
  • builddirectory : 빌드 대상 요소를 찾는 공간을 의미합니다. ${basedir}은 기본값으로 실행될 앤트파일이 담긴 디렉토리가 되는데, 그것의 부모폴더로 기술했으므로 워크스페이스의 위치가 될 것입니다. (빌드 프로젝트의 부모는 워크스페이스이므로)

나머지 자세한 속성들은 이클립스 온라인 도움말에서 확인하십시오.

eclipse.refreshLocal 태그는 워크스페이스를 갱신하는 역할을 합니다. 앤트는 이클립스와 독립적인 플랫폼이어서 IResource를 이용하여 작업하지 않습니다. 표준 File API 나 커맨드 라인 명령도구를 이용하므로, 워크스페이스의 싱크가 깨집니다. 빌드 작업이 끝나면 꼭 이 태스크 태그를 이용하여 워크스페이스를 갱신 시켜 주어야 합니다.

새로 추가한 타겟을 실행해 보면, HelloAntPlugin 프로젝트 밑에 build.xml 과 javaCompiler...args 파일이 추가된 것을 알 수 있습니다.

생성된 스크립트 실행 해 보기

생성된 build.xml을 열어 보면 뒷골이 당겨 올 겁니다. 이 앤트스크립트를 역공학 하실 수 있는 분은 시도해 보시기 바랍니다.

생성된 build.xml을 열어서, 아웃라인 뷰에서 "@dot"를 선택하고 우클릭 한 뒤, [Debug As] / [Ant Build ...]을 선택 한 뒤, 실행 구성에서 앞서 말씀드린 것 처럼 Workspace 와 동일한 JRE를 사용하게 한 상태에서 실행 해 봅시다. 이 타깃은 소스를 컴파일 하는 역할을 합니다.

실행이 끝나면 이번에는 build.update.jar를 선택 한 뒤 다시 실행합니다.

실행이 완료되면, 플러그인 프로젝트를 우클릭 하고, [Refresh]를 선택하여 워크스페이스를 갱신합니다. eclipse.buildScript가 생성한 스크립트는 무인 실행용이어서, 사람이 결과물을 확인하지 않기 때문에 eclipse.refreshLocal을 자동으로 호출해주지 않습니다. 워크스페이스를 갱신해보면 위 그림처럼, 플러그인이 빌드된 jar파일과, 각 컴파일된 자바 클래스 파일이 구조대로 담긴 @dot 폴더가 생겼을 것입니다.

지금은 우리가 생성된 스크립트를 사람 손으로 실행 했지만, 이번에는 메인스크립트가 스크립트를 생성한 뒤 자동으로 실행하게 해 봅시다.

다른 스크립트 호출하기

이제 원래 빌드프로젝트로 돌아와 HelloAnt.build.xml을 다시 열고 build-plugin-test-2라는 타깃을 만들고 다음의 내용을 추가합니다:

 

	스크립트 생성중
	<eclipse.buildScript elements="plugin@HelloAntPlugin" baselocation="${eclipse.home}" builddirectory="${basedir}/../" />

	스크립트 실행중
	<ant antfile="../HelloAntPlugin/build.xml" target="@dot" inheritall="false" />
	<ant antfile="../HelloAntPlugin/build.xml" target="build.update.jar" inheritall="false" />
	<eclipse.refreshLocal resource="/" depth="infinite" />

ANT 태스크 태그

ant 태그의 속성은 매우 직관적입니다. antfile은 실행할 팔일을, 그리고 타깃을 지정했습니다. inheritall의 값으로 false를 줬는데, 이것은 메인스크립트에 선언된 프로퍼티를 호출될 스크립트에게 전달하겠느냐는 의미가 됩니다. 자식스크립트는 ${basedir}이 자기자신이 있는 곳이라고 생각하고 작성되었을 것이기 때문에, 보통은 프로퍼티를 상속해 주는 것은 위험합니다. 명시적으로 필요한 속성만 상속하는 것이 좋습니다. 이 방법은 나중에 소개합니다.

새로만든 타깃을 실행 해 보면, 플러그인 프로젝트 밑에 jar가 생성되는 것을 확인 할 수 있습니다.

수고하셨습니다, 다음 시간에는 feature.xml로 부터 모든 플러그인을 자동빌드하고 피쳐역시 빌드하는 방법과, 찌꺼기 파일들을 안전하게 제거하는 방법에 대해 알아보겠습니다.

좋아요를 누르지 않으면 이 씨리즈의 다음 문서가 업데이트 되지 않을 수도 있습니다.

Posted by 지이이이율
,

위와 같은 구조 [os/플랫폼명/아키텍쳐명/]로 폴더를 만들고 DLL을 추가하면 됩니다. 물론 다른 플랫폼에서는 SO나 다른 라이브러리 파일을 추가하셔야 합니다.

플랫폼 명 및 아키텍트명에 대한 상수가 org.eclipse.core.runtime.Platform 에 선언되어있으니 참고하시기 바랍니다.

Posted by 지이이이율
,

1. 빌드된 제품으로 실행 할 때만 몇몇 기능이 작동하지 않는 경우

이클립스는 디버그 모드에서 작동할 때, 워크벤치 타겟 플랫폼(개발 타겟)에 있는 플러그인 들 + 워크스페이스 내에 있는 개발중인 플러그인들의 조합으로 수행 됩니다.

그런데, 개발중인 플러그인들은 배포 될 때, 모든 파일이 포함되는 것이 아니라 빌드 설정에 포함된 파일만 빌드 됩니다. 반면 디버그 중에는 이러한 설정과 무관하게 워크스페이스에 있는 모든 파일이 포함되어 실행되므로, 두 결과에 차이가 발생합니다.

예를 들어 개발 중일 때는 포토샵 파일이 있을 수 있지만, 빌드시에는 랜더링된 JPG파일만 나가면 됩니다. 이러한 이유에 의해, 기본적으로 프로젝트에 속한 모든 파일이 빌드되지는 않습니다. 여러분이 프로젝트에 폴더, 아이콘등을 추가할 때, 이 파일이 런타임에 필요한지 여부를 판단하고 빌드 설정에 추가하는 습관을 가지도록 하세요.

2. 제품 업데이트 후 일부 기능이 작동하지 않음

마찬가지로 디버그할 때는 잘작동하는 프로그램이, 런타임에서 업데이트 사이트를 거쳐 업데이트 이후 제대로 동작하지 않는 경우가 있습니다. 대표적인 증세로는 NoSuchMethodException 같은 것이 있습니다. 원인은 간단합니다. 플러그인의 버전을 올려주지 않았기 때문에, 일부 플러그인이 동일한 버전으로 판단, 업데이트가 이루어 지지 않은 것입니다.

'PDE' 카테고리의 다른 글

빌드 자동화 하기 #1 - Hello Ant  (2) 2011.03.07
이클립스 플러그인에 DLL 포함시키기  (0) 2010.12.10
Bundle과 Resource  (0) 2010.10.28
네이쳐와 빌더  (0) 2010.10.15
당신의 Editor는 몇 점입니까?  (0) 2010.10.08
Posted by 지이이이율
,

Bundle과 Resource

PDE 2010. 10. 28. 18:33

플러그인 내부로 부터 파일을 읽어들이는 작업은, 개발자들이 자주 실수를 저지르는 부분입니다. 많은 사람들이 플러그인의 위치를 알아내어 File을 읽어 들이려고 합니다. 그러나 이러한 접근법은 많은 문제를 일으킬 수 있습니다

Bundle bundle = MyPlugin.getDefault().getBundle(); 
URL resource = bundle.getResource("/icon/test.gif");
Image image = new Image(null, resource.openStream());

뭐 물론 구글해 보면, 위와 같이 번들을 이용해서 리소스를 사용해야 한다는 것은 쉽게 알 수 있습니다. 이 문서가 설명하려는 것은 왜 그렇게 해야 하는가 하는 것입니다.

플러그인은 생각보다 역동적입니다.

우선 플러그인은 jar로 묶여 있을수도 있고 아닐 수도 있습니다. 또, 다른 플러그인들과 같은 위치에 있을수도 있고 그렇지 않을 수도 있습니다. 예를 들어 PDE를 디버깅 모드로 실행하면, Target에 지정된 이클립스의 플러그인들과 작업 중이던 워크스페이스에 있는 플러그인들이 로드됩니다.

뿐만아니라 플러그인은 여러개의 프로젝트로 구성될 수 있으며, 한 개의 jar가 아니는 여러 jar로 구성될 수도 있습니다. (Fragment Project)

게다가 리소스도 동적이에요!

다국어 지원 매커니즘을 번들이 어떻게 지원하는지 간단히 알아 봅시다.

번들에 포함된 모든 리소스는 번들을 통해서 로드됩니다. 여러분이 작성한 아이콘, html 도움말 파일, toc.xml목차등과 같이 플러그인에 담긴 모든 리소스들도 그러합니다. 번들은 리소스를 찾을 때 다음과 같은 매커니즘을 가집니다:

개발자 -> 번들: /icon/test.gif 내놔
번들 -> 이클립스: 지금 사용자 로케일이 뭐냐?
이클립스 -> 번들: ko (한글)
번들 -> 번들 :nl/ko/icon/test.gif 탐색
alt 찾은 경우
번들 -> 개발자: nl/ko/icon/test.gif
else 없는 경우
번들 -> 번들: icon/test.gif 탐색
번들 -> 개발자: icon/test.gif
end

이는 이해를 돕기 위한 그림으로, 정확히 이와 같이 동작하지는 않습니다. 하지만 이렇게 이해해도 좋습니다. 이 이외에도 번들을 통한 리소스 탐색은 몇몇 매커니즘이 있지만, 이 정도면 왜 그렇게해야 하는지는 충분히 이해가 되셨으리라 믿습니다.

Posted by 지이이이율
,

네이쳐와 빌더

PDE 2010. 10. 15. 11:42

네이쳐

네이쳐(Nature)는 한 프로젝트가 갖는 일부 성질들에 대한 추상성을 제공한다. 예를 들어 한 개의 플러그인 프로젝트는, 자바 프로젝트의 특성과, PDE의 특성을 갖는다. 이런 프로젝트의 특성을 네이쳐라고 하며, 한 프로젝트는 여러개의 네이쳐를 가질 수 있다.

네이쳐의 주임무는 다음과 같다.

  • 프로젝트가 해당 특성을 갖기 위해 필요한 자원의 준비와 해제
  • 필요한 경우, 프로젝트의 빌드 방법을 플랫폼에 알려줌

네이쳐의 선언

   
      
         
      
      
      
   

최근 RCP등과 더불어 추가된 확장점들은 확장 노드(<extension />)가 아닌 그 하위 IConfigurationElement에서 상세 기술 및, ID지정을 많이 하지만, 네이쳐는 확장노드 그자체에 이름과 ID가 기술된다. 오래전 부터 존재하던 확장점들은 대체로 이런 성격이다. 익스텐젼 노드 자체가 네이쳐 노드란 사실을 빨리 눈치채지 못하면 네이쳐 노드를 만들려고 헤맬수도 있다.

3라인에서 지정된 네이쳐 클래스는 IProjectNature 인터페이스를 구현해야 하며, 네이쳐가 설치되거나 해제될 때 처리해야 할 일을 맡는다. 5라인에서 연관된 빌더 ID를 지정해 줄 수 있지만, 네이쳐 확장점 자체가 빌더를 관리해 주지는 않으며, 빌더절에서 다시 설명한다. required-nature 노드를 이용하여 디펜던시를 지정할 수 있다. 네이쳐 추가/삭제시 조건이 만족되지 않으면 Exception을 발생시켜준다.

프로그래밍으로 네이쳐 추가

IProject p = ...

IProjectDescription description = p.getDescription();
List<String> natureIds = new ArrayList<String>();

// 기존 네이쳐 아이디들 추가
natureIds.addAll(Arrays.asList(description.getNatureIds()));
natureIds.add("추가할 네이쳐 아이디");

// 프로젝트 디스크립션 갱신
description.setNatureIds(natureIds.toArray(new String[natureIds.size()]));

// 프로젝트에 새로운 디스크립션 반영
p.setDescription(description, new NullProgressMonitor());

프로젝트를 변경 할 때는 IProjectDescription을 얻어 수정 한 다음, 프로젝트에 다시 그 디스크립션을 넘김으로서 반영한다. 새로운 네이쳐가담긴 디스크립션이 프로젝트에 주어질 때 다음과 같은 일이 일어난다.

Project -> NatureManager: 네이쳐 구성 요청
NatureManager -> 네이쳐: (de)configure
네이쳐 -> Project: 구성

네이쳐 Configuration

네이쳐가 프로젝트에 추가되면, 네이쳐 객체는 configure 메소드를 콜백 받는다. 이때 필요한 런타임 라이브러리를 클래스패스에 추가한다던가, 빌더를 구성한다던가와 같은 작업을 수행 할 수 있다. 이 작업은 대칭성을 이루어 deconfigure()를 통해 해제된다. 하지만, 네이쳐의 추가와 삭제는 FacetedProject와 달리 사용자가 자유자재로 추가/삭제할 수 있는 UI가 없기 때문에, 규칙의 강제성은 적은 편이다. 네이쳐는 위저드 내지, 프로그래머가 작성한 코드에 의해서만 추가 삭제된다.

빌더

빌더란 워크스페이스에 리소스 변경이 있을 때마다 (자동 빌드의 경우) 또는 사용자가 명시적으로 빌드 요청을 내릴 때, 모든 프로젝트들에 대해 주어진 빌드 스펙에 따라 결과물을 만들어내는 개체들이다. 이 과정중에 문제가 생기면 리소스 마커를 이용하여 에러나 경고등을 문제 뷰에 나타나게 하기도 한다.

빌더의 선언


  
    
    
  

나머지 속성은 직관적이므로 생략하고, isConfigurable이라는 것은 사용자가 직접 증분, 클린, 풀 빌드를 선택할 수 있는지 여부를 체크하게 하겠느냐라는 것이다. 프로젝트의 속성 다이얼로그의 빌더 항목에 표시된다.

빌더 클래스의 주요 인터페이스

protected IProject[] build(int kind, Map args, IProgressMonitor monitor);
public ISchedulingRule getRule(int kind, Map args);

1번라인이 주 진입점으로 실제로 빌드 작업을 수행해야 하는 곳이다. 리턴값은 빌드에 의하여, 다른 프로젝트가 영향을 받아, 다시 빌드해야 하는 경우를 위한 것으로 Frozen될 때 까지 반복되니 주의하자. kind는 빌드 타입을 나타내는 것으로 전체 빌드인지, 증분 빌드인지, 클린 빌드인지등의 정보가 넘어오며, 맵을 통해 옵션들이 전달된다. 두번째 라인의 스케쥴링 룰은 이 빌드 작업이 다른 작업과 배타적으로 동작하기 위한 스케쥴링 룰을 정의 한다. 기본 구현은 워크스페이스 록이다. 스케쥴링 룰에 대해서는 Job과 스케쥴링 룰문서를 참조할 수 있다.

빌드 스펙 구성

빌드관련 기능을 가진 네이쳐는 프로젝트를 configure()할 때, 프로젝트의 빌드 스펙을 변경하여 빌더를 사용할 수 있도록 해야 한다. 프로젝트들은 빌더 커맨드라는 것을 가지고 있는데, 프로젝트의 컨텐츠가 변경될 때마다(자동 빌드의 경우) 가지고 있는 커맨드들을 수행하게 된다. 이러한 커맨드의 집합을 프로젝트의 빌드 스펙이라고 한다.

빌더 커맨드

public interface ICommand {
	public Map getArguments();
	public String getBuilderName();
	public boolean isBuilding(int kind);
	public boolean isConfigurable();
	public void setArguments(Map args);
	public void setBuilderName(String builderName);
	public void setBuilding(int kind, boolean value);
}

결국 빌더 커맨드는 어떤 빌더에게 어떤 변수들을 가지고 빌드를 시킬 것이냐 하는 명세서 객체이다. 빌더 커맨드는 IProjectDescription의 newCommand()로 새로 만들 수 있다. 주의 할 점은 get/set builderName의 인자는 빌더 확장점에서 주었던 ID이다. 이름이 아니다. 확장점에서 준 이름은 UI에서만 표시되고, 코드상으로 빌더 이름은 ID를 의미한다.

description.setBuildSpec(newCommands);
getProject().setDescription(description, new NullProgressMonitor());

프로젝트가 원래가지고 있던 빌더 커맨드 리스트에 새 커맨드를 확장하여, 디스크립션에 추가한다음, 디스크립션을 프로젝트에 제출하면, 이제 이 프로젝트는 주어진 빌더 커맨드에 의해, 리소스가 변경될 때마다 빌더를 호출한다.

Faceted Proejct

프로젝트 네이쳐는 프로젝트에 대하여 상대적으로 정적이다. 예를 들어 자바 프로젝트가 C++ 프로젝트로 변모하는 일은 없다. 하지만 J2EE프로젝트에 스프링, 스트러츠, 태그 라이브러리등의 추가삭제는 훨씬 더 빈번하게 일어난다. 네이쳐의 정적 구조로는 이들에 대응하기 힘들기 때문에, 웹 파생 프로젝트 특성은 Nature가 아닌 Facet으로 처리한다. 따라서 관련 니즈가 있는 사람들은 Faceted Project에 관한 문서들을 찾아 보면 된다. 해당 소스는 BeA가 대부분 기여했는데, IBM과 달리 주석이 빈약하고, 소스 변동이 심해 고생을 좀 각오해야한다. IBM만세. 나 일좀 시켜줘.

Posted by 지이이이율
,

사실 에디터란, 편집되는 대상에 따라 평가 방식이 달라지기 때문에, 그 전체에 대해 품질 점검표를 만들 수는 없습니다. 본 기사에서는 이클립스로 만들어진 에디터로서 지켜야 할 기본 품질 속성에 대해 알아봅니다. 100점 부터 시작하여, 자신이 만든 에디터가 감점 요소에 해당되면 해당 점수만 큼 감점하시면 됩니다.

ps. 이클립스를 배우면 배울 수록 기존의 이클립스에 있는 에디터들이 얼마나 잘 만들어졌나에 심한 충격을 받게 됩니다. 그래도 힘 냅시다.

1. File 선택 동기화 : 5점

  1. 에디터를 두 세개 정도 엽니다.
  2. 리소스 네비게이터나, 패키지 익스플로러 뷰는 좌우화살표 모양의 Link with Editor 버튼이 있습니다. 이 버튼을 누릅니다.
  3. 활성 에디터와 뷰의 파일 선택이 동기화 되어야 합니다.

만약 에디터의 입력이 복수의 파일등으로 구성되는 경우라면, 아마도 여러분은 직접 IEditorInput을 만드셨을 겁니다. 이 경우에는 에디터와 IEditorInput이 IFile.class에 대한 어댑터 질의에 응답하여야지만 그러한 기능이 정상작동합니다.

2. 에디터 복원 : 5점

  1. 본인이 만든 에디터를 두 세개 정도 엽니다.
  2. 이클립스를 종료하고 다시 실행합니다.
  3. 열어 두었던 모든 에디터가 되살아나 있어야 합니다.

IFileEditorInput 등과 같은 경우만 입력으로 고려한 경우, 이러한 내용은 모두 저절로 처리 되어 잘 알기 어렵지만, 실제로 에디터의 입력은 뷰에 표시되는 한 모델 객체 일 수도 있습니다. 예를 들면 MyLyn의 태스크 엘리먼트들은 파일이 아닙니다. 이러한 경우엔, 입력을 보존하고, 복원하는 작업을 직접 수행해야 합니다.

보존과정

워크벤치 -> IEditorInput : getPersistable
IEditorInput --> 워크벤치: IPersistable
워크벤치 -> IPersistable: getFactoryId
워크벤치 -> 워크벤치: id를 이용하여 메멘토 노드 생성
워크벤치 -> IPersistable: saveState
IPersistable-> IPersistable: 입력 상태 저장

복원 과정

워크벤치 -> 워크벤치: id로 팩토리 탐색
워크벤치 -> 엘레멘트 팩토리: 메멘토 노드 전달
엘레멘트 팩토리 -> 엘레멘트 팩토리 : IEditorInput  복원
엘레멘트 팩토리 -> 워크벤치: IEditorInput
워크벤치 -> 워크벤치 : 에디터 복원

메멘토 노드는 일종의 XML 노드입니다. 워크벤치는 종료될때 XML로 종료직전의 상황을 기록합니다. 팩토리는 Element Factory 확장점을 이용하여 등록합니다.

3. 파일 동기화: 총 배점 20

  1. 에디터를 엽니다.
  2. 텍스트 에디터등 다른 에디터로도 그 파일을 엽니다.
  3. 다른 에디터에서 수정한 뒤 저장합니다.
  4. 원래 에디터를 활성화 합니다.
  5. 더티 마크가 없던 경우, 파일을 즉시 다시 읽어 들여 표시해야 하고, 더티마크가 있는 경우, 파일의 내용이 바뀌었으니 다시 읽겠냐고 질의해야 합니다. : 10점
  6. 에디터가 열린 채로 해당 파일을 삭제하거나, 부모 폴더를 삭제하거나, 프로젝트를 삭제하거나 Close합니다. 이 때 에디터는 사라져야 합니다. : 5점
  7. 에디터가 열린 상태로 파일을 리네임 하거나 다른 곳으로 이동시킵니다. 에디터에서 조금 작업을 한 뒤 저장하면, 바뀐 파일로 안전하게 저장되야 하며, Save as... 기능도 작동해야 합니다. : 5점

이 기능은 SVN, CVS등 업데이트, get contents등과 같은 기능이 제대로 동작하는데에도 매우 중요합니다. 입력이 IFileEditorInput이었던 경우, 다음과 같이 워크스페이스를 리스닝 해야 합니다. 왜 해당 파일이 아니라, 전체 워크스페이스를 감시해야 하는지는 이어서 설명합니다.

if (input instanceof IFileEditorInput) {
	ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
}

해당 리소스에 영향을 미치는 방법은 아주 다양합니다. 프로젝트 닫기, 프로젝트 리네임, 부모 폴더 이동, 부모폴더 삭제, 부모폴더 리네임등등 경우의 수가 매우 다양합니다. 따라서, 워크스페이스 전체의 이벤트를 감시한다음, 그것을 해당 리소스를 기준으로 의미를 번역하여 사용합니다.

public void resourceChanged(IResourceChangeEvent e) {
	IFileEditorInput input = (IFileEditorInput) getEditorInput();
	IFile file = input.getFile();
	IPath fullPath = file.getFullPath();

	// 프로젝트가 닫긴경우. bug 8593
	if (e.getDelta() == null) {
		return;
	}

	IResourceDelta relatedDelta = e.getDelta().findMember(fullPath);
	if (relatedDelta == null) {
		return;
	}

	int kind = relatedDelta.getKind();

	if (kind == IResourceDelta.REMOVED) {
		handleResourceRemoved(relatedDelta);
	}

	if (kind == IResourceDelta.CHANGED && !this.isSaving) {
		handleResourceChanged(relatedDelta);
	}
}

11 번째 라인이 리소스 변경 이벤트를, 입력 파일을 기준으로 상대적 이벤트로 변환하는 코드입니다.

4. 문서 동기화 : 10점

  • 에디터를 엽니다.
  • 메뉴의 Window / New Window 를 선택하여, 워크벤치 윈도우를 하나 더 엽니다.
  • 새 윈도우에서도 같은 파일에 대한 같은 에디터를 엽니다.
  • 어느 한쪽을 수정하면 즉시 동일한 컨텐츠를 보여 주어야 합니다.
  • 어느 한쪽에서 저장하면, 나머지 한쪽에서도 더티마크가 사라져야 합니다.

이클립스 텍스트 기반 에디터는 DocuemntProvider를 통해서 문서를 공급받습니다. 따라서, JavaEditor와 Text Editor를 동시에 열어 둔 경우, 둘은 같은 도큐먼트를 공급받게 되어 수정 즉시, 저장하지 않도라도 동일한 내용을 표시하게 됩니다. 에디터가 파일로부터 모델을 직접만드는 방식으로 구현해서는 안됩니다.

여러개의 파일로부터 나온 모델(문서)들이 서로 참조를 갖는 경우, 한 파일로 부터 하나의 모델만 공급되는 중간 체계가 있어야 하는것은 매우 중요합니다. 그렇지 않다면, 모델단위로 쉽게 구현될 수 있었던 리팩토링이, 파일 변경이벤트로만 처리되어야 하므로 매우 어렵고 복잡한 문제로 바뀔 것입니다.

GEF 에디터의 기본구현에 포함된 EditDomain은 EditorPart를 하나만 갖으면서 CommandStack을 내장하고 있기 때문에, 이 사항을 충족하는데는 상당히 까다롭고 높은 비용이 듭니다. ㅠㅠ

5. 사용자 단축키 지정: 10점

  • 메뉴에서 Windows / Preference 를 엽니다.
  • 필터에서 key라고 입력하고, key 노드를 선탣합니다.
  • 우측 필터에 에디터의 이름을 입력합니다.
  • 에디터와 연관된 모든 단축키가 즉시 알아볼 수 있는 형태로 나타나야 하며 변경할 수 있어야 합니다. 예를 들어 '삭제', '추가'와 같은 형태로 나타난다면 사용자는 "뭘 삭제하고 추가한단 말이지?" 라고 생각할 것입니다.
  • 변경한 단축키는 노출된 모든 경로에 즉시 표시되고 적용되야 합니다. (팝업 메뉴 등등)
  • When 필드에서 "in My Editor" 와 같은 여러분이 만든 에디터를 사용중일때를 의미하는 컨텍스트가 공급되야 합니다.
  • 여러분의 에디터를 사용하는 도중 Ctrl+3을 누르고, "삭제", "~추가" 등과 같은 명령어를 직접 타이핑 하여 해당 명령을 실행 시킬 수 있어야 합니다.: 예를 들어 자바 에디터에서 필드를 선택한 뒤 Ctrl+3을 누르고 generate라고 입력해 보십시오.

이클립스 3.2의 주요한 변화는 액션이 가벼워 졌다는 점입니다. IAction객체는 UI생성과 비즈니스 로직-run()이 함께 있기 때문에 매우 무겁습니다. 단지 툴바에 버튼을 하나 그리기 위해서 클래스가 로드되고, 그로 인해 플러그인까지 시작되어 버리기 때문에, 이클립스의 시작시간 아주 느리게 만듭니다. 이클립스는 어마어마하게 많은 플러그인들이 전사적으로 뒤섞여 동작하기 때문에, 이 룰을 따르지 않으면 치명적인 결과가 나타납니다. 이러한 명령들은 모두 Command라고 부르는 확장점으로 선언적으로 설계되어야 합니다. 그러면 이클립스는 XML파일을 로드하는 것 만으로도 UI를 만들 수 있고, 플러그인은 시작되지 않아도 됩니다. 또한 이렇게 선언적으로 만들어진 명령들은, 자동적으로 단축키 설정등과 같은 플랫폼의 지원을 받을 수 있게 됩니다. 이클립스 위키에서 Command Framework, Core Expression, Menu Contribution, UIContext를 참조하십시오.

6. Team Support: 10점

  • 새파일을 만들고 에디터를 엽니다.
  • SVN등에 이 프로젝트를 셰어하고 커밋합니다.
  • 문서 변경, 저장 커밋을 몇 차례 반복합니다.
  • history 뷰를 열어, 과거버전을 더블 클릭하면 제대로 열려야 하며, *읽기 전용*으로 열려야 합니다.
  • 읽기 전용으로 열린 과거 버전의 파일을 Save as... 로 저장하면, 편집이 가능해져야 합니다.
  • 만약 이전 버전과의 비교 에디터를 공급했다면 당신은 이 평가표를 읽을 필요도 없는 구루이십니다. 부끄럽게 왜 그럽시니까. 원츄-3-b

7. 클립보드, 프린팅: 10점

  • 당신의 에디터에서 일부 모델을 Copy 합니다.
  • 메모장에 붙여 넣으면 텍스트로, 워드에 붙여 넣으면 스타일 텍스트로, 그림판에 붙여 넣으면 그림으로 붙여 넣어져야 합니다.
  • 인쇄가 되어야 합니다.

'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
Posted by 지이이이율
,