
이클립스 기반 구조는 플러그인 모델을 통해 확장성과 유연성을 제공합니다. 이 글에서는 이클립스 플러그인 개발의 기본 구조와 디버깅 방법에 대해 설명합니다. 플러그인의 구조, 모델, 디버깅 과정을 이해하여 이클립스 플러그인을 효과적으로 개발하고 관리할 수 있습니다.
이클립스는 단일 집적(monolithic) 프로그램이 아니라 플러그인 로더(Plug-in loader)라는 다소 작은 커널이며 수백 개 이상의 플러그인에 둘러싸여 있다. 이런 모듈화된 설계 덕분에 이클립스의 개별 기능 조각들을 재사용함으로써 이클립스의 원래 개발자가 생각하지 못했던 다양한 애플리케이션을 누구나 개발할 수 있다.
이클립스는 모듈식 아키텍쳐를 가지고 있으며, 모든 기능을 플러그인으로 제공
각 플러그인은 독립적인 단위로 개발되고, 특정 기능을 제공하며, 다른 플러그인과 상호작용
플러그인의 종속성과 서비스는 MANIFEST.MF와 plugin.xml 파일에서 선언
이클립스가 시동될 때는 플러그인 로더가 각 플러그인에 대한 MANIFEST.MF와 plugin.xml 파일 전부를 훑고, 플러그인에 대한 정보를 포함하는 구조체를 구성
[MANIFEST.MF]
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Favorites Plug-in
Bundle-SymbolicName: com.qualityeclipse.favorites; singleton:=true
Bundle-Version: 1.0.0
Bundle-Activator: com.qualityeclipse.favorites.FavoritesActivator
Bundle-Vendor: QualityEclipse
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime
Bundle-ActivationPolicy: lazy
Export-Package: com.qualityeclipse.favorites.views
Bundle-RequiredExecutionEnvironment: J2SE-1.5
이 파일은 OSGi(오픈 서비스 게이트웨이 이니셔티브) 번들 매니페스트 파일입니다. 이 파일은 플러그인의 메타데이터를 포함하며, 플러그인의 구성 및 의존성을 정의합니다.
Manifest-Version: 1.0
매니페스트 파일의 버전을 지정합니다. OSGi와 관련된 표준 버전입니다.
Bundle-ManifestVersion: 2
번들 매니페스트의 버전을 지정합니다. OSGi 사양에서 버전 2는 추가된 기능과 구조적 개선을 제공합니다.
Bundle-Name: Favorites Plug-in
플러그인의 이름을 지정합니다. 사용자 친화적 이름으로, 플러그인을 식별하는 데 사용됩니다.
Bundle-SymbolicName: com.qualityeclipse.favorites; singleton:=true
플러그인의 고유 식별자입니다. 자바 패키지 네이밍 규칙을 따르며,
singleton:=true는 플러그인이 싱글톤임을 나타냅니다.
Bundle-Version: 1.0.0
플러그인의 버전을 지정합니다. 주버전, 부버전, 수버전 형식을 따릅니다.
Bundle-Activator: com.qualityeclipse.favorites.FavoritesActivator
플러그인의 생명 주기를 관리하는 번들 활성화기 클래스를 지정합니다. OSGi 프레임워크에서 번들이 시작되거나 중지될 때 호출됩니다.
Bundle-Vendor: QualityEclipse
플러그인의 제공자를 지정합니다. 개발자 또는 회사 이름입니다.
Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime
플러그인이 의존하는 다른 번들을 지정합니다. 여기서는 org.eclipse.ui와 org.eclipse.core.runtime 번들에 의존합니다.
Bundle-ActivationPolicy: lazy
플러그인의 활성화 정책을 지정합니다. lazy는 플러그인이 필요할 때(즉, 클래스가 로드되거나 리소스가 접근될 때) 활성화됨을 나타냅니다.
Export-Package: com.qualityeclipse.favorites.views
다른 번들이 사용할 수 있도록 내보내는 패키지를 지정합니다. 여기서는 com.qualityeclipse.favorites.views 패키지를 내보냅니다.
Bundle-RequiredExecutionEnvironment: J2SE-1.5
플러그인이 실행될 자바 실행 환경을 지정합니다. 여기서는 Java 2 Standard Edition 1.5를 필요로 합니다.
[plugin.xml]
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<plugin>
<extension
point="org.eclipse.ui.views">
<category
name="QualityEclipse"
id="com.qualityeclipse.favorites">
</category>
<view
name="Favorites"
icon="icons/sample.gif"
category="com.qualityeclipse.favorites"
class="com.qualityeclipse.favorites.views.FavoritesView"
id="com.qualityeclipse.favorites.views.FavoritesView">
</view>
</extension>
</plugin>
이 파일은 이클립스 플러그인의 확장 지점을 정의합니다. 플러그인이 이클립스 플랫폼과 상호작용하는 방법을 지정합니다.
<?xml version="1.0" encoding="UTF-8"?>
<eclipse version="3.2"?>
<plugin>
<extension point="org.eclipse.ui.views">
확장 지점입니다. 이클립스 플랫폼에서 org.eclipse.ui.views 확장 지점을 확장합니다.
이 확장 지점은 새로운 뷰를 추가할 수 있게 합니다.
<category>
name="QualityEclipse": 카테고리의 이름입니다.id="com.qualityeclipse.favorites": 카테고리의 고유 ID입니다.<view>
새로운 뷰를 정의합니다.
name="Favorites": 뷰의 이름입니다.
icon="icons/sample.gif": 뷰의 아이콘 파일 경로입니다.
category="com.qualityeclipse.favorites": 뷰가 속하는 카테고리의 ID입니다.
class="com.qualityeclipse.favorites.views.FavoritesView": 뷰를 구현하는 클래스의 전체 경로입니다.
id="com.qualityeclipse.favorites.views.FavoritesView": 뷰의 고유 ID입니다.
여기서, 알고 가야할 부분!
종속 관계와 확장 개념
종속 관계
MANIFEST.MF의 Require-Bundle 항목에서 org.eclipse.ui와
org.eclipse.core.runtime 번들을 지정하여 Favorites 플러그인이 이 번들들에 의존함을 나타냅니다.
plugin.xml에서는 org.eclipse.ui.views 확장 지점을 통해 이클립스 플랫폼과 상호작용합니다.
확장
plugin.xml의 <extension> 요소를 통해 이클립스의 뷰 확장 지점을 확장하고, 새로운 뷰를 추가합니다.
<category>와 <view> 요소를 통해 새로운 뷰의 속성 및 동작을 정의합니다.
이클립스는 플러그인을 링크 파일(.link)을 통해 참조할 수 있음
링크 파일은 플러그인 디렉토리의 위치를 지정
플러그인은 플러그인 디렉토리와 JAR 파일 형태로 제공
이클립스는 두 가지 형식을 혼합하여 사용
플러그인 선언은 플러그인의 메타데이터를 정의합니다.
플러그인 ID
플러그인 버전
플러그인 이름과 제공자
플러그인 클래스 선언
플러그인 런타임 설정은 플러그인의 실행 환경을 정의합니다. 이 부분은 플러그인이 실행될 때 필요한 클래스, 라이브러리 및 리소스를 지정합니다. MANIFEST.MF 파일의 여러 항목은 런타임 설정을 명시합니다.
Bundle-Activator: com.qualityeclipse.favorites.FavoritesActivator
이 항목은 번들의 활성화기 클래스를 지정합니다. 활성화기 클래스는 번들이 시작되고 중지될 때 호출됩니다.
FavoritesActivator 클래스는 플러그인의 생명 주기를 관리하는 역할을 합니다. 이를 통해 플러그인은 시작 및 중지 시 필요한 초기화 및 정리 작업을 수행할 수 있습니다.
Bundle-ActivationPolicy: lazy
이 항목은 플러그인의 활성화 정책을 정의합니다. lazy 정책은 플러그인이 필요할 때(예: 클래스가 처음 로드되거나 리소스가 처음 요청될 때) 활성화됨을 나타냅니다.
이 설정은 성능을 향상시키기 위해 사용됩니다. 필요할 때까지 플러그인이 활성화되지 않기 때문에 메모리 사용량을 줄일 수 있습니다.
Export-Package: com.qualityeclipse.favorites.views
이 항목은 다른 번들이 접근할 수 있는 패키지를 정의합니다. 여기서는 com.qualityeclipse.favorites.views 패키지를 외부에 공개합니다.
이 설정은 다른 플러그인이 이 패키지에 포함된 클래스와 인터페이스를 사용할 수 있도록 합니다.
Bundle-RequiredExecutionEnvironment: J2SE-1.5
이 항목은 플러그인이 실행될 자바 실행 환경을 지정합니다. 여기서는 Java 2 Standard Edition 1.5를 필요로 합니다.
이 설정은 플러그인이 특정 자바 버전 이상에서만 실행되도록 보장합니다.
플러그인 종속성은 플러그인이 제대로 작동하기 위해 의존하는 다른 플러그인들을 명시합니다. MANIFEST.MF 파일에서 종속성 설정은 Require-Bundle 항목을 통해 정의됩니다.
Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime
이 항목은 플러그인이 의존하는 다른 번들을 나열합니다. 여기서는 org.eclipse.ui와 org.eclipse.core.runtime 번들에 의존하고 있습니다.
org.eclipse.ui: 이 번들은 이클립스의 UI 관련 기능을 제공합니다. 플러그인이 사용자 인터페이스와 상호작용할 수 있도록 도와줍니다.
org.eclipse.core.runtime: 이 번들은 이클립스의 기본 런타임 인프라를 제공합니다. 플러그인이 실행되는 데 필요한 핵심 기능을 포함합니다.
종속성 관리
컴파일 시점: 종속성은 플러그인을 컴파일하는 동안 필요합니다. 이클립스는 컴파일 시점에 필요한 클래스와 리소스를 찾기 위해 종속성을 확인합니다.
실행 시점: 종속성은 플러그인을 실행할 때도 필요합니다. 종속된 번들이 로드되지 않으면 플러그인이 제대로 실행되지 않을 수 있습니다.
이클립스 플러그인 아키텍처의 핵심 개념 중 하나는 확장(Extension)과 확장점(Extension Point)입니다. 이를 통해 플러그인 간에 기능을 공유하고 확장할 수 있습니다
org.eclipse.ui.views가 확장점입니다.<extension point="org.eclipse.ui.views">을 통해 확장점에 확장을 정의합니다.플러그인 클래스는 플러그인의 생명 주기를 관리하는 역할을 합니다. 이 플러그인에서는 FavoritesActivator 클래스가 플러그인의 시작과 종료 시 수행할 작업을 정의합니다. 이 클래스는 AbstractUIPlugin을 확장하여 필요한 기능을 제공합니다.
플러그인이 시작되고 종료될 때 수행되는 작업을 정의하는 부분은 주로 start와 stop 메서드에서 처리
[FavoritesActivator.java]
public class FavoritesActivator extends AbstractUIPlugin {
// 플러그인 ID를 나타내는 상수
public static final String PLUGIN_ID = "com.qualityeclipse.favorites";
// 공유 인스턴스를 저장하는 변수
private static FavoritesActivator plugin;
// 생성자: 기본 생성자
public FavoritesActivator() {
}
/**
* 플러그인이 활성화될 때 호출되는 메서드.
*
* @param context 번들의 실행 컨텍스트
* @throws Exception 예외가 발생할 경우
*/
public void start(BundleContext context) throws Exception {
super.start(context);
// 공유 인스턴스를 현재 인스턴스로 설정
plugin = this;
}
/**
* 플러그인이 비활성화될 때 호출되는 메서드.
*
* @param context 번들의 실행 컨텍스트
* @throws Exception 예외가 발생할 경우
*/
public void stop(BundleContext context) throws Exception {
// 공유 인스턴스를 null로 설정하여 해제
plugin = null;
super.stop(context);
}
// 공유 인스턴스를 반환하는 메서드
public static FavoritesActivator getDefault() {
return plugin;
}
/**
* 주어진 플러그인 상대 경로의 이미지 파일에 대한 이미지 디스크립터를 반환하는 메서드.
*
* @param path 이미지 파일의 경로
* @return 이미지 디스크립터
*/
public static ImageDescriptor getImageDescriptor(String path) {
return imageDescriptorFromPlugin(PLUGIN_ID, path);
}
}
주의할 점!!!
start()와 stop() 메소드와 같이 메소드 오버라이드 할 때는 몇 가지 사항을 주의해야 합니다. 항상 상위 클래스의 메소드 구현도 꼭 호출해야 하고, 이클립스의 시동이나 종료 과정에서 시간이 오래 소요되거나 메모리를 과다하게 사용하지 않도록 최소한의 작업만을 수행해야 합니다.
Plugin 클래스와 AbstractUIPlugin 클래스는 플러그인 동작을 정의하는 데 사용됩니다.
Plugin 클래스
AbstractUIPlugin 클래스
[AbstractUIPlugin에서 제공하는 기타 메소드]
getImageDescriptor(String path)
getPreferenceStore()
getDialogSettings()
이클립스 플랫폼은 플러그인과 그 동작을 관리합니다.
플러그인은 번들(Bundle)로서 관리되며, 각 번들은 독립적으로 배포되고 관리됩니다.
플러그인의 확장과 확장점을 관리하는 레지스트리입니다.
로깅은 플러그인의 상태를 추적하고 오류를 기록하는 중요한 부분입니다. Eclipse에서는 로깅을 통해 플러그인의 상태와 문제점을 기록하고 분석할 수 있습니다. 주로 상태 객체와 오류 로그 뷰를 사용합니다.
상태 객체는 플러그인의 상태를 나타내는 데 사용됩니다. Eclipse는 IStatus 인터페이스와 Status 클래스를 사용하여 상태 객체를 관리합니다. 상태 객체는 주로 로그에 기록될 메시지를 포함하며, 상태의 심각도(severity)와 관련된 정보를 가지고 있습니다.
IStatus 인터페이스: 상태 객체의 기본 인터페이스입니다.
Status 클래스: IStatus 인터페이스를 구현하는 기본 클래스입니다.
[상태 객체의 주요 필드와 메서드]
severity: 상태의 심각도를 나타냅니다. (예: IStatus.OK, IStatus.INFO, IStatus.WARNING, IStatus.ERROR)
plugin: 상태가 발생한 플러그인의 ID
message: 로그에 기록될 메시지
exception: 예외가 발생한 경우, 해당 예외 객체
[FavoritesLog.java]
package com.qualityeclipse.favorites;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
/**
* The logger of convenience for the Favorites plug-in.
*/
public class FavoritesLog {
/**
* Log the specified information.
*
* @param message, a human-readable message, localized to the
* current locale.
*/
public static void logInfo(String message) {
log(IStatus.INFO, IStatus.OK, message, null);
}
/**
* Log the specified error.
*
* @param exception, a low-level exception.
*/
public static void logError(Throwable exception) {
logError("Unexpected Exception", exception);
}
/**
* Log the specified error.
*
* @param message, a human-readable message, localized to the
* current locale.
* @param exception, a low-level exception, or <code>null</code>
* if not applicable.
*/
public static void logError(String message, Throwable exception) {
log(IStatus.ERROR, IStatus.OK, message, exception);
}
/**
* Log the specified information.
*
* @param severity, the severity; one of the following:
* <code>IStatus.OK</code>,
* <code>IStatus.ERROR</code>,
* <code>IStatus.INFO</code>, or
* <code>IStatus.WARNING</code>.
* @param code, the plug-in-specific status code, or
* <code>OK</code>.
* @param message, a human-readable message, localized to the
* current locale.
* @param exception, a low-level exception, or <code>null</code>
* if not applicable.
*/
public static void log(int severity, int code, String message,
Throwable exception) {
log(createStatus(severity, code, message, exception));
}
/**
* Create a status object representing the specified information.
*
* @param severity, the severity; one of the following:
* <code>IStatus.OK</code>,
* <code>IStatus.ERROR</code>,
* <code>IStatus.INFO</code>, or
* <code>IStatus.WARNING</code>.
* @param code, the plug-in-specific status code, or
* <code>OK</code>.
* @param message, a human-readable message, localized to the
* current locale.
* @param exception, a low-level exception, or <code>null</code>
* if not applicable.
* @return, the status object (not <code>null</code>).
*/
public static IStatus createStatus(int severity, int code, String message,
Throwable exception) {
return new Status(severity, FavoritesActivator.PLUGIN_ID, code,
message, exception);
}
/**
* Log the given status.
*
* @param status, the status to log.
*/
public static void log(IStatus status) {
FavoritesActivator.getDefault().getLog().log(status);
}
}
[FavoritesLog.java의 각 메소드 역할]
logInfo(String message)
message - 사람 읽기 쉬운 메시지.FavoritesLog.logInfo("Initialization complete");logError(Throwable exception)
exception - 발생한 예외.try {
// some code that throws an exception
} catch (Exception e) {
FavoritesLog.logError(e);
}log(int severity, int code, String message, Throwable exception)
severity - 심각도 (IStatus.OK, IStatus.INFO, IStatus.WARNING, IStatus.ERROR).code - 플러그인 특정 상태 코드 (일반적으로 IStatus.OK 사용).message - 사람 읽기 쉬운 메시지.exception - 발생한 예외. (null일 수 있음) FavoritesLog.log(IStatus.WARNING, IStatus.OK, "Potential issue detected", null);
createStatus(int severity, int code, String message, Throwable exception)
severity - 심각도.code - 플러그인 특정 상태 코드.message - 사람 읽기 쉬운 메시지.exception - 발생한 예외. (null일 수 있음) IStatus status = FavoritesLog.createStatus(IStatus.ERROR, IStatus.OK, "Error occurred", new Exception());
log(IStatus status)
status - 기록할 상태 객체. IStatus status = FavoritesLog.createStatus(IStatus.ERROR, IStatus.OK, "Error occurred", new Exception());
FavoritesLog.log(status);
오류 로그 뷰는 Eclipse IDE 내에서 플러그인의 오류와 경고 메시지를 표시하는 UI 컴포넌트입니다. 플러그인의 상태 객체가 로그에 기록되면, 오류 로그 뷰에서 이를 확인할 수 있습니다.
오류 로그 뷰 열기:
Eclipse IDE에서 Window > Show View > > Other... > General > Error Log를 선택하여 오류 로그 뷰를 엽니다.
오류 로그 뷰 인터페이스:
