p2

이클립스가 번들들을 설치, 업데이트 또는 관리를 할 때 사용하는 프로비져닝 플랫폼을 P2라고 합니다. 근본적으로, P2는 이클립스 또는 에퀴녹스 기반의 어플리케이션을 프로비져닝하고 관리하는 기술입니다.

Agent

클라이언트에 설치된 프로비져닝 인프라는 주로 Agent라고 언급됩니다. 에이전트는 자기자신이나 다른 프로필을 관리할 수 있습니다. 에이전트는 이클립스 시스템과 별도로 작동할 수 있으며, 다른 이클립스 시스템에 임베딩된 형태일 수도 있습니다. 에이전트는 필요한 경우 여러 프로필을 관리할 수있으며, 반대로 한 시스템이 여러개의 에이전트를 가질수도 있습니다. *P2 에이전트*라고하는 것은 실제로 존재하지 않습니다. P2란 모듈일 뿐이기 때입니다. 임베디딩 시스템이나, 데스크탑 또는 서버에서 P2 에이전트를 사용할 때 각기 다른 모듈이 사용됩니다.

Artifact

아티팩트들은 설치되거나 관리될 실제 컨텐츠를 말합니다. 번들의 JAR파일 또는 실행파일이 대표적인 아티팩트입니다.

Artifact Repository

아티팩트들을 담고 있는 리포지터리를 의미합니다.

Director

디랙터란 Planner와 Engine에서 일어나는 일들에 대한 상위 수준 API입니다. 즉, 디렉터는 플래너에게 프로비져닝 작업들을 수행하게끔 명령을 내리고, 그 결과를 엔진에게 전달하고 적용하게 하여, 필요한 프로비져닝 작업을 수행하게 합니다.

엔진

엔진은 디렉터가 결정한 필요한 프로비져닝 오퍼레이션들을 실제로 수행할 책임을 갖습니다. 디렉터의 주 작업의 주제가 메타데이터인 반면, 엔진의 관심사는 디렉터가 선정한 IU(Installation Unit)들에 포함된 아티팩트 및 구성 정보입니다. 엔진은 필요한 아티팩트를 필요한 위치에서 사용할 수 있도록 리포지터리 및 전송 매커니즘에 협조하게 됩니다.

이클립스 위키에서 엔진 보기

가비지 컬렉션

알려진 루트로 부터 접근성 추적을 통하여, 불필요한 리포지터리의 요소(메타데이터와 아티팩트)들은 수집되어 파기될 수 있습니다. 예를 들어, 에이전트에 의해 관리되는 모든 프로필들은 프로비져닝 에이전트가 직접적으로 관심을 갖는 모든 IU들을 식별할 수 있습니다. 마찬가지로 IU역시 프로필을 실행하기 위해 필요한 아티팩트들을 식별할 수 있습니다. 전이 목록에 포함되지 않은 IU나 아티팩트들은 쓰레기로 취급되며 수집됩니다.

Installation Unit(IU)

IU는 설치될 것들에 대한 정보를 담은 *메타데이터* 이며, 실제로 설치되는 것들을 의미하지 않습니다. 따라서, 번들은 IU가 아니고, 단지 번들의 이름, 버전, 캐퍼빌리티, 디펜던시 등등을 담은 디스크립션입니다. 번들의 JAR는 아티팩트입니다.

이클립스 위키에서 IU 보기

메타데이터 리포지터리

IU들을 담고 있는 메타데이터 리포지터리.

미러링

분산의 기본 오퍼레이션은 미러링입니다.

Phase

프로비져닝 오퍼레이션은 보통 여러 과정(페이즈)에 걸쳐 특정작업을 수행하던 중 일어납니다. 각 과정(Phase)마다 특정한 종류의 활동이 일어납니다. 구성 단계(Configure Phase)에 런타임 시스템과 접점(Touchpoint)의 상태에 따라, 동적으로 다양한 아티팩트를 필요로하게 될 것이고, 그에 따라 동적으로 Fetch Phrase가 수행될 것입니다.

플래너

플래너는 주어진 프로필을 요청받은대로 재 구성하는데 필요한 작업들을 결정합니다. 다시 말해, 프로필의 현재 상태와 목표 상태, 그리고 메타데이터(가용한 IU들에 대한 정보를 담은)를 이용하여 프로비져닝 오퍼레이션들이 담긴 리스트를 만들어 냅니다. (예: 인스톨, 업데이트 또는 언인스톨).

Touchpoint

P2에서 터치포인트는 P2 프로비져닝 시스템과 특정 런타임 및 관리 시스템을 통합하는 임무를 가지며, 엔진의 일부입니다. 예를 들어 이클립스 터치포인트는 에퀴녹스 스토어를 이해하고, 번들을 관리합니다. 다른 플랫폼은 다른 네이티브 터치포인트 구현을 이용하여 통합합니다. 더 다양한 터치포인트의 예제를 보려면 이곳(영문)을 클릭하십시오.

'Eclipse Core' 카테고리의 다른 글

아아 좋은 이클립스 삽질이다  (0) 2010.11.01
Extension Point and Extension  (0) 2010.07.08
어댑터 디커플링  (2) 2009.08.10
Job과 Schedule Rule  (0) 2009.08.08
Posted by 지이이이율
,

SWT에는 크게 두 종류의 클래스들이 있습니다. 하나는 UI를 추상화하는 클래스들로 이들은 모두 Widget을 상속받습니다. 나머지는 Resource를 상속받는 클래스들로 운영체제 GDI자원을 추상화 합니다.

Widget#dispose()

위짓을 디스포즈한다는 것은, 운영체에게 해당 UI의 핸들을 돌려주는 것을 의미합니다. 운영체제는 부모의 핸들만 반환 받으면, 그 핸들의 UI에 붙어 있던 자식들도 함께 회수하기 때문에, 일일히 자식까지 디스포즈 할 필요가 없습니다.

위짓의 디스포즈 절차

개발자 -> Composite : dispose()
Composite -> Composite : dispose evnet
Composite -> 자식들 : release()
자식들 -> 자식들 : dispose event
자식들 --> 개발자 :

위에서 보이는 것과 같이, 자식들의 dispose() 메서드는 호출되지 않고, 단지 dispose 이벤트만 발송됩니다. Canvas나 Composite를 상속받는 경우, 부모가 파기될 때 dispose() 메서드가 호출될 것이라 생각하고, GDI자원들을 dispose하는 코드들을 dispose()안에 작성해두면 릭이 생기니 주의가 필요합니다. 대신 다음과 같은 형태로 디스포즈 하여야 합니다.

addDisposeListener(new DisposeListener() {	
	@Override	
	public void widgetDisposed(DisposeEvent e) {
		disposeResource();  // 이 컨트롤이 사용하는 리소스들을 dispose하는 메서드
	}
});

Resource#dispose()

리소스는 UI인 위짓과 달리, 트리 형태의 부모 자식 구조가 없습니다. 이러한 GDI자원에는 컬러, 커서, 폰트, 그래픽 컨텍스트, 폴리곤, 패턴, 텍스트레이아웃, 어파인 트랜스폼등이 있습니다. 색상을 예로 들면, 빨강이라는 색깔은 여러 UI가 함께 공유해서 사용하고 있을 수 있습니다. 따라서, 리소스들은 반드시 개발자가 불필요한 시점을 판단하여 dispose()를 직접 호출 해 주어야 합니다.

JFace

JFace를 처음 시작하는 사람들은 JFace를 SWT의 일부로 착각하는 경우가 많습니다. 예를 들어 Window나 Dialog를 SWT의 Shell과 비슷한 것으로 생각하곤 합니다. 하지만 JFace는 SWT 위짓을 이용하여 원하는 UI를 대신 만들어주는 일종의 팩토리라고 볼 수 있습니다.

예를 들어 TreeViewer는 일일히 TreeItem을 만들고 아이콘, 텍스트를 지정하는 대신, 개발자가 공급한 컨텐트 프로바이더와 레이블 프로바이더를 이용하여 그 작업을 대신 해 줍니다. 또 다른 예를 들면 Action과 같은 추상화된 비즈니스 객체를 ToolbarManager는 알아서 Toolbar와 ToolItem을 만들고 Toolitem에 눌림 이벤트를 후킹해 비즈니스 로직과 연결해 줍니다.

마찬가지로 Window나 Dialog도 개발자가 Shell을 쉽게 만들수 있도록 도와주는 클래스이며, 어떤 SWT클래스도 상속받지 않습니다. 이렇게 UI를 쉽게 만들게 해 주는 것 이외에 JFace의 또 다른 임무는 컨트롤링 코드를 쉽게 만드는 것입니다.

예를들어 Person 객체의 name이 변경되어 해당 트리 아이템에 text를 다시 지정해야 하는 경우, SWT만으로 처리하려면, 개발자는 해당 모델을 표현하는 TreeItem의 맵을 직접만들고 관리하며, 찾아내어 일일히 setText를 호출해야 합니다. 이러한 저수준 UI코드가 컨트롤링 코드에 포함되면, 가독성이 크게 훼손되고 안전한 개발을 해 나가기 어렵습니다. TreeViewer는 이런 경우 간단하게 TreeViewer#update(Object element) 메서드를 제공하여 특정 모델을 표현하는 TreeItem을 간단하게 갱신할 수 있게 해 줍니다.

정리하면 JFace의 가장 중요한 두가지 역할은:

  • SWT를 이용하여 UI를 쉽게 구성할 수 있개 해줌
  • 컨트롤러 코드를 작성을 간단하게 만들어 줌
이라고 할 수 있습니다.

JFace의 소스들을 참고하여, 여러분이 만드는 에디터나, 복잡한 컨트롤들도 유사 패턴으로 작성해 보세요. 좋은 훈련이 됩니다.

JFace의 디스포즈 패턴

모든 JFace 클래스들이 그런 것은 아니지만, 대부분의 JFace 클래스들이 SWT를 이용하여 UI를 생성하고, 그것들을 쉽게 관리하게 하는 것을 기본으로 하고 있기 때문에, JFace 객체의 생명주기는 그들이 만들어 낸 UI와 대부분 일치합니다. JFace는 UI를 만들면서 보통 많은 GDI자원들을 함께 사용합니다. 따라서 JFace 스타일의 코드들은 UI의 dispose 이벤트를 받으면 사용되었던 모든 자원을 반환합니다.

TreeViewer의 디스포즈 예

Tree -> TreeViewer : dispose event
TreeViewer -> LabelProvider  : dispose
LabelProvider -> LabelProvider : 사용된이미지 및 색상 정리
LabelProvider -> TreeViewer : 
TreeViewer -> TreeViewer : unmap 
TreeViewer --> Tree :

결론

위 예제에서도 나타나는 것처럼 JFace의 요소인 LabelProvider는 dispose() 메서드를 갖고 있지만 LabelProvider는 위짓도 리소스도 아닙니다. dispose()의 의미도 전혀 다릅니다. 이들을 착각하게 되면, 릭이나 미궁에 빠지기 십상입니다.

다시 한번 정리하면 dispose()란:

  • SWT 위짓: 자기자신과 자기자신에 붙어있는 하위 위젯들 모두 Dispose 이벤트를 발송하게 한 뒤, 최초 dispose 메서드를 호출받은 위짓만 핸들을 운영체제 반환
  • SWT 리소스: 운영체제에게 빌려온 GDI 자원을 운영체제에게 반환
  • JFace: 자기 자신이 SWT를 이용하여 UI등을 구축하는데 사용했던 모든 자원을 반환 및 정리

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

우선, 아무리 뛰어난 사람도, 처음 맡는 도메인의 문제를 해결해야 할 때는, 삽질이라는 절차를 반드시 거쳐야 합니다. 이 때는 정보의 추상적 깊이, 내포 관계등을 구분할만한 단서가 없으므로, 질문 하기 보다는, 그 분야의 개요를 빨리 익혀야 합니다. 매크로 뷰가 없는 상태에서 질문과 답변을 반복하면, 부분적으로 얕은 지식이 쌓이게 됩니다. 이는 전체를 오해하는 결과를 초래 해, 일반적으로 오히려 해가 됩니다. 가능한한 짧고 간략한 오버뷰 문서를 구하여, 정독합니다.

검색

이클립스는 세상에서 가장 커다란 오픈 프로젝트이며, 따라서 방대한 양의 좋은 문서들이 모두 공개되어 있습니다. 좋은 검색 습관을 갖는 것 만으로도, 당신의 생산성을 10배이상 높일 수 있습니다.

  • 만약 겪고 있는 문제가 SWT / JFace 관련이라면 스니펫을 검색한다. - 모든 링크는 아래에 있습니다.
  • 이클립스에서 널리 사용되고 있는 유형의 문제라면 이클립스 공식 FAQ를 검색한다.
  • 일반적인 큰 개요 유형의 문제라면, 이클립스 위키 문서 또는 리소스를 검색 해 본다.
  • 다른 사람도 겪고 있는 문제라는 생각이 들면, 구글을 검색 해 본다.
  • 구글에 없으면 이클립스 뉴스 그룹에 들어간다. 대개의 질문은 RCP그룹에서 답을 얻을 수 있다.
  • 그곳에서도 답이 없다면 이클립스 뉴스 그룹에 질문한다. 거기엔 월급을 받고 *헌신*적으로 대답해 주는 사람들이 있다. 이곳에서 2~3일 이내(주말 제외)에 답변을 얻지 못하면 다른 돌파구를 마련해야 합니다.

좋은 대답을 가려내는 것은 매우 어려운 일입니다. 알려진 유명인이 대답한 경우에는 (예를 들어 폴 웹스터나 에드먹은 관련 카테고리 내에서 신의 대접을 받고 있습니다) 문제가 없지만 그렇지 않은 경우에는, 가려운 점을 정확히 긁어 주지 못하거나, 국소적인 질문에 대해 너무 큰 개요의 대답을 받는 경우도 종종있습니다. 이럴 때는 상대방이 왜 그런 대답을 했는지 곰곰히 생각 해 보는 것이 중요합니다.

질문을 할 때에는 자신의 의도와 하려는 일을 명확히 기술 하십시오. 대부분의 삽질은 *방법*이나 *시도*자체가 잘못되었을 가능성이 높습니다. 단지 API만 질문하는 것보다 훨씬 폭 넓은 조언을 얻을 수 있습니다.

영어에 대한 두려움을 버리십시오. 이클립스 커뮤니티 포럼에 작성되는 글의 50%가량은 엉터리 영어입니다. (물론 나머지는 그냥 영어죠) 이클립스는 글로벌이니까요. ㅎㅎ. 게다가 컴퓨터 영어는 생각보다 쉽습니다. 단순한 단어를 짧게 조합해서 궁금한 것만 확실히 전달해도 다들 친절하게 대답해 줍니다. 이클립스에 관한 유용하고 훌륭한 정보는 거의 전부라고 해도 좋을 정도로 영어로 작성되 있습니다. 이클립스 5년차 한국 개발자 보다, Java밖에 모르지만 모르지만 영어를 아는 사람이 훨씬 더 유리할 정도로 말이죠.

ps. SWT는 의외로 같은 연구소 내의 MFC/win32개발자들에게 질문하면 간단히 납득할 수 있는 대답을 얻는 경우가 많았습니다.

역공학

문제가 너무 특수하여 대답을 받을 수 없는 경우, 이클립스 내에서 비슷한 기능을 찾아 본다.

  • 해당 기능을 실행하는 다이얼로그등에서 Alt + Shift + F3을 누르면 어떤 플러그인이 그 UI를 기여했는지 파악이 가능하다.
  • 그 상태에서 다시 Alt + Shift + F1을 누르면 그 다이얼로그나 현재 보이는 UI의 활성 클래스 및, UI 컨텍스트, 셀렉션 모델등에 대해 확인이 가능하다. 적당한 클래스를 열어 브레이크포인트를 찍고 관찰하자.
  • Alt + Shift + F2를 누른 상태에서 메뉴나 툴바 아이템을 누르면, 그 메뉴의 메뉴 컨트리뷰션 주소를 알 수 있다.
  • ICommandService 로부터 커매드가 제출 될 때 마다, 어떤 핸들러가 선택되는지 확인 하는 간단한 플러그인을 만들어, 무엇이 실제로 문제를 해결하고 있는지 디버깅해 볼 수 있다.
  • 모든 익스텐젼 포인트는 우클릭을 하여 Find Reference를 선택하면 이 익스텐젼 포인트를 어떻게 사용하는지 알 수 있다. (우선 용의자 플러그인들을 디펜던시에 넣어둘 것)

관련 링크들

'Eclipse Core' 카테고리의 다른 글

P2의 주요 용어 정리  (0) 2010.11.29
Extension Point and Extension  (0) 2010.07.08
어댑터 디커플링  (2) 2009.08.10
Job과 Schedule Rule  (0) 2009.08.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 지이이이율
,

마우스 클릭 시나리오

마우스 -> Tool: 클릭
Tool -> EditPart: 드래그 트래커 요청
EditPart --> Tool: 드래그 트래커
Tool -> DragTracker : 마우스 이벤트
DragTracker -> DragTracker : 리퀘스트 생성
DragTracker ->EditPart: 리퀘스트
EditPart -> EditPolicy*: 리퀘스트 전달
EditPolicy* -> EditPart: Command
EditPart -> Tool: Command
Tool -> Tool: 커맨드 수행
Tool -> 마우스: 복귀

개체의 역할 설명

  • Tool: 대게의 그래픽 편집이 그러하듯, UI이벤트는 일차적으로 툴에게 전달됩니다. 툴은 상태기반 머신의 일종으로, 대게는 마우스 아래에 있는 에디트파트나, 현재 선택된 에디트 파트와 연동하게 됩니다. 이 때, 그 연동의 역할을 수행하는 것이 DragTracker입니다. Selection Tool은 EditPart에게 드래그 트래커를 요청하고, 이벤트를 전달합니다. 예를 들어 선택된 에디트파트의 드래그 트래커에게 마우스가 눌렸다는 사실을 통보하고, 이 드래그 트래커는 선택 요청(SelectionRequest) 객체를 만들게 됩니다.
  • DragTracker: 드래그 트래커는 Selection Tool을 도와주는 객체입니다. 다른 툴들은 보통 드래그 트래커를 사용하지 않습니다. 대부분의 작업은 Selection Tool에 의해 이루어지므로(선택, 이동, 크기 조절, 입양, 직접 편집 삭제 등등), 드래그 트래커는 매우 중요한 역할을 하며, 각 파트의 동작을 개인화 하는 주요 진입점이 됩니다. 드래그 트래커는 셀렉션 툴이 전달한 UI이벤트를 바탕으로, Request 객체를 만들어 EditPart에 전달합니다.
  • 에디트 파트는 최상위 CEO급 컨트롤러이므로, 이 시퀀스 다이어그램에서 나타난 롤만을 설명합니다. 에디트 파트는 Request를 받으면, 자기 자신이 가진 편집 정책(EditPolicy)들에게 이 요구를 전달하고, 편집 정책들이 특정 행동을 하게 하거나, 정책들이 반환 해준 Command를 다시 툴에게 전달합니다.
  • EditPolicy: 기본적으로는 요구를 받으면, 해당 요구를 분석하여, 모델을 수정하는 커맨드를 만들어 리턴합니다. 독자적으로 호스트를 감시하면서 동작을 개인화 하기도 합니다.
  • 사족

    Posted by 지이이이율
    ,

    SWT에서 IME의 상태는 같은 Shell안에 있을 경우 보장 받습니다. 대부분의 셀 편집이나, 텍스트 필드들은 워크벤치 윈도우의 액티브 셸안에 있기 때문에, 한영상태를 유지하는데 문제가 없습니다. 그러나 입력 다이얼로그등이 열리는 순간 한영 상태를 잃게 되어 사용자의 시간을 많이 빼았게 됩니다. 이 문제를 해결 해 봅시다.

    이 테크닉은 한글IME가 있는 윈도우즈 제품군에서만 확인되었습니다. 이 코드는 반드시 운영체제가 윈도우인지 체크하고 사용하세요. 다른 운영체제에서 실험 해 보신 분은 결과를 알려주시면 고맙겠습니다.

    영문인지 확인하는 방법

    Shell s = ...
    
    boolean inEngilsh = s.getImeInputMode() == SWT.NONE;
    

    영문으로 지정하는 방법

    Shell s = ...
    
    s.setImeInputMode(SWT.NONE);
    

    한글인지 확인하는 방법

    Shell s = ...
    
    boolean inKorean = s.getImeInputMode() == SWT.Native;
    

    한글로 지정하는 방법

    Shell s = ...
    
    s.setImeInputMode(SWT.ALPHA | SWT.PHONETIC);
    

    그렇습니다. 이상합니다. 상식적으로는 setImeInputMode에서 SWT.NATIVE로 지정해야 할 것 같지만, 그렇게 해 보면 부작용으로 영어및 숫자가 전각으로 입력됩니다. 물론 이상하긴 합니다만, 우리 개발자들은 한글을 쓰는 서러움이 익숙하리라 믿습니다.

    Posted by 지이이이율
    ,

    How to Customize EMF Loading

    EMF 2010. 9. 27. 15:02
    EMF문서는 일반적으로 굉장히 복잡한 과정을 거쳐 파일로 부터 EObject 문서를 만들어 내기 때문에 어느지점을 수정해야할지 막막하기 그지없습니다. 주요역할을 하는 개체로는 리소스, Load, 로드 핸들러, 헬퍼가 있으며, 그 흐름은 다음과 같습니다.

    EMF 문서의 읽기의 개요

    ResourceImpl -> LoadImpl: doLoad
    LoadImpl -> LoadHandler: SAX Events
    LoadHandler -> XMLHelper: Deligates
    XMLHelper -> XMLHelper : Build EObject DOM
    XMLHelper -> ResourceImpl: 
    

    ResourceImpl

    리소스 객체는 기본적으로 EObject 문서를 시리얼라이즈 디시리얼라이즈 하는 진입점이 됩니다. 또한 리소스셋과 함께 멀티플 리소스의 관리도 이루어집니다.

    • createXMLLoad() / createXMLSave() : XMLLoad및 XMLSave를 만듭니다.
    • createXMLHelper() : XMLLoad및 XMLSave를 도와줄 XMLHelper를 만듭니다.

    XMLLoadImpl

    입력스트림을 SAX 처리하여, 문서를 구성하고 노드들을 탐색하며 에러등을 처리합니다. 대부분의 작업은 LoadHandler에 위임됩니다.

    • makeDefaultHandler() : SAX이벤트에 대응하여 문서를 작성하는 핸들러를 생성합니다.
    • handleErrors() : 문서의 진단 정보를 어떻게 처리할 것인지를 오버라이드 할 수 있습니다.

    LoadHandler

    SAXParser 이벤트 리스너와 유사합니다. 파일을 읽는 동안 진행상태등을 표시하려면 이것을 오버라이드 해야 합니다. save, load 시에 전달되는 Map에 IProgressMonitor등을 넣어두는 테크닉을 이용합니다.

    • sax이벤트 메소드들
    • 레퍼런스 업데이트

    XMLHelper

    XML과 EObject DOM사이의 최후의 중재자입니다. 로드 핸들러로부터 요청받아 DOM을 만들고, 속성을 지정하는 등의 작업을 수행합니다.

    질문은 메일로만 받고 있습니다. jeeeyul@지메일.com

    'EMF' 카테고리의 다른 글

    EMF, XML 1.1 읽어들이기 문제  (0) 2011.04.18
    EMF 트랜잭션 관리  (0) 2009.06.08
    Posted by 지이이이율
    ,