[Eclipse] 실전 플러그인 개발: 플러그인 구조와 디버깅 가이드

Ma_Seokjae·2024년 7월 16일
post-thumbnail

이클립스 기반 구조는 플러그인 모델을 통해 확장성과 유연성을 제공합니다. 이 글에서는 이클립스 플러그인 개발의 기본 구조와 디버깅 방법에 대해 설명합니다. 플러그인의 구조, 모델, 디버깅 과정을 이해하여 이클립스 플러그인을 효과적으로 개발하고 관리할 수 있습니다.

기반구조 개요

이클립스는 단일 집적(monolithic) 프로그램이 아니라 플러그인 로더(Plug-in loader)라는 다소 작은 커널이며 수백 개 이상의 플러그인에 둘러싸여 있다. 이런 모듈화된 설계 덕분에 이클립스의 개별 기능 조각들을 재사용함으로써 이클립스의 원래 개발자가 생각하지 못했던 다양한 애플리케이션을 누구나 개발할 수 있다.

플러그인 구조와 작업 공간

1. 플러그인 구조

  • 이클립스는 모듈식 아키텍쳐를 가지고 있으며, 모든 기능을 플러그인으로 제공

  • 각 플러그인은 독립적인 단위로 개발되고, 특정 기능을 제공하며, 다른 플러그인과 상호작용

  • 플러그인의 종속성과 서비스는 MANIFEST.MFplugin.xml 파일에서 선언

  • 이클립스가 시동될 때는 플러그인 로더가 각 플러그인에 대한 MANIFEST.MFplugin.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"?>
  • XML 선언부입니다. XML 버전과 인코딩을 지정합니다.
<eclipse version="3.2"?>
  • 이클립스 버전을 지정합니다. 이 플러그인이 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> 요소를 통해 새로운 뷰의 속성 및 동작을 정의합니다.

2. 작업 공간

  • 작업 공간은 사용자가 작업하는 모든 리소스를 포함하는 디렉토리
  • 프로젝트, 소스 파일, 설정 파일 등이 저장됨

플러그인 디렉토리와 JAR 파일

1. 링크 파일

  • 이클립스는 플러그인을 링크 파일(.link)을 통해 참조할 수 있음

  • 링크 파일은 플러그인 디렉토리의 위치를 지정

2. 혼합 방식

  • 플러그인은 플러그인 디렉토리와 JAR 파일 형태로 제공

  • 이클립스는 두 가지 형식을 혼합하여 사용


플러그인 내역서

1. 플러그인 선언

  • 플러그인 선언은 플러그인의 메타데이터를 정의합니다.

    플러그인 ID

    • 플러그인을 식별하는 고유한 ID
    • 일반적으로 자바 패키지 이름 규약으로 작성
      => com.<회사명>.<제품명>

    플러그인 버전

    • 플러그인의 버전을 지정
    • 항상 3개의 수를 점으로 구분해 나열함
      • 첫 번째 수는 주 버전(major version), 두 번째는 부 버전(minor version), 세 번째는 서비스 레벨을 의미

    플러그인 이름과 제공자

    • 플러그인의 이름과 제공자를 명시

    플러그인 클래스 선언

    • 플러그인의 메인 클래스를 선언

2. 플러그인 런타임

플러그인 런타임 설정은 플러그인의 실행 환경을 정의합니다. 이 부분은 플러그인이 실행될 때 필요한 클래스, 라이브러리 및 리소스를 지정합니다. 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를 필요로 합니다.

  • 이 설정은 플러그인이 특정 자바 버전 이상에서만 실행되도록 보장합니다.

3. 플러그인 종속성 (Plugin Dependencies)

플러그인 종속성은 플러그인이 제대로 작동하기 위해 의존하는 다른 플러그인들을 명시합니다. MANIFEST.MF 파일에서 종속성 설정은 Require-Bundle 항목을 통해 정의됩니다.

Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime

  • 이 항목은 플러그인이 의존하는 다른 번들을 나열합니다. 여기서는 org.eclipse.uiorg.eclipse.core.runtime 번들에 의존하고 있습니다.

  • org.eclipse.ui: 이 번들은 이클립스의 UI 관련 기능을 제공합니다. 플러그인이 사용자 인터페이스와 상호작용할 수 있도록 도와줍니다.

  • org.eclipse.core.runtime: 이 번들은 이클립스의 기본 런타임 인프라를 제공합니다. 플러그인이 실행되는 데 필요한 핵심 기능을 포함합니다.

종속성 관리

  • 컴파일 시점: 종속성은 플러그인을 컴파일하는 동안 필요합니다. 이클립스는 컴파일 시점에 필요한 클래스와 리소스를 찾기 위해 종속성을 확인합니다.

  • 실행 시점: 종속성은 플러그인을 실행할 때도 필요합니다. 종속된 번들이 로드되지 않으면 플러그인이 제대로 실행되지 않을 수 있습니다.

4. 확장(Extension)과 확장점(Extension Point)

이클립스 플러그인 아키텍처의 핵심 개념 중 하나는 확장(Extension)과 확장점(Extension Point)입니다. 이를 통해 플러그인 간에 기능을 공유하고 확장할 수 있습니다

1. 확장점과 확장의 관계

  • 확장점 정의:
    • 이클립스 플랫폼이나 다른 플러그인에서 제공됩니다. 여기서는 org.eclipse.ui.views가 확장점입니다.
    • 이 확장점은 플러그인이 새로운 UI 뷰를 추가할 수 있도록 허용합니다.
  • 확장 정의:
    • plugin.xml에서 <extension point="org.eclipse.ui.views">을 통해 확장점에 확장을 정의합니다.
    • 이 확장은 새로운 뷰를 추가하며, 뷰의 카테고리, 이름, 아이콘, 클래스 등을 정의합니다.
    • 확장은 이클립스 플랫폼이 제공하는 확장점에 새로운 기능을 추가합니다.

플러그인 클래스

플러그인 클래스는 플러그인의 생명 주기를 관리하는 역할을 합니다. 이 플러그인에서는 FavoritesActivator 클래스가 플러그인의 시작과 종료 시 수행할 작업을 정의합니다. 이 클래스는 AbstractUIPlugin을 확장하여 필요한 기능을 제공합니다.

1. 시동과 종료

  • 플러그인이 시작되고 종료될 때 수행되는 작업을 정의하는 부분은 주로 startstop 메서드에서 처리

    [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() 메소드와 같이 메소드 오버라이드 할 때는 몇 가지 사항을 주의해야 합니다. 항상 상위 클래스의 메소드 구현도 꼭 호출해야 하고, 이클립스의 시동이나 종료 과정에서 시간이 오래 소요되거나 메모리를 과다하게 사용하지 않도록 최소한의 작업만을 수행해야 합니다.

2. 플러그인 이른 시동하기

  • 플러그인을 이른 시점에 시작하도록 설정할 수 있음

3. 정적 플러그인 리소스

  • 정적 리소스를 플러그인에 포함시키는 방법을 정의

4. 플러그인 환경설정

  • 플러그인의 환경설정을 관리하는 방법을 다룸

5. 플러그인 구성 파일

  • 플러그인의 구성 파일을 정의하고 관리

6. Plugin과 AbstractUIPlugin

  • Plugin 클래스와 AbstractUIPlugin 클래스는 플러그인 동작을 정의하는 데 사용됩니다.

    Plugin 클래스

    • Plugin 클래스는 Eclipse 플러그인의 기본 클래스입니다. 플러그인의 시작, 종료, 리소스 관리 등의 기본 기능을 제공합니다. Plugin 클래스를 직접 상속받아 플러그인을 구현할 수 있지만, UI 관련 기능이 필요한 경우 AbstractUIPlugin 클래스를 사용하는 것이 일반적입니다.

    AbstractUIPlugin 클래스

    • AbstractUIPlugin 클래스는 Plugin 클래스를 확장하여, Eclipse UI와 통합된 플러그인을 쉽게 구현할 수 있도록 추가적인 기능을 제공합니다. 주로 UI 관련 플러그인을 구현할 때 사용됩니다. AbstractUIPlugin은 Plugin의 기능을 모두 포함하며, UI 관련 메서드를 추가로 제공합니다.

[AbstractUIPlugin에서 제공하는 기타 메소드]

getImageDescriptor(String path)

  • 주어진 경로의 이미지 파일에 대한 ImageDescriptor를 반환합니다. UI에서 이미지를 로드하는 데 사용됩니다.

getPreferenceStore()

  • 플러그인의 기본 환경설정 저장소를 반환합니다. 플러그인에서 환경설정을 관리할 때 사용됩니다.

getDialogSettings()

  • 플러그인의 대화 상자 설정을 반환합니다. 플러그인에서 대화 상자의 상태를 저장하고 복원할 때 사용됩니다.

플러그인 모델

1. Platform

이클립스 플랫폼은 플러그인과 그 동작을 관리합니다.

2. 플러그인과 번들

플러그인은 번들(Bundle)로서 관리되며, 각 번들은 독립적으로 배포되고 관리됩니다.

3. 플러그인 확장 레지스트리

플러그인의 확장과 확장점을 관리하는 레지스트리입니다.


로깅

로깅은 플러그인의 상태를 추적하고 오류를 기록하는 중요한 부분입니다. Eclipse에서는 로깅을 통해 플러그인의 상태와 문제점을 기록하고 분석할 수 있습니다. 주로 상태 객체와 오류 로그 뷰를 사용합니다.

1. 상태 객체 (Status Object)

상태 객체는 플러그인의 상태를 나타내는 데 사용됩니다. 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 객체.
  • 사용 예시:
 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);

2. 오류 로그 뷰 (Error Log View)

오류 로그 뷰는 Eclipse IDE 내에서 플러그인의 오류와 경고 메시지를 표시하는 UI 컴포넌트입니다. 플러그인의 상태 객체가 로그에 기록되면, 오류 로그 뷰에서 이를 확인할 수 있습니다.

  • 오류 로그 뷰 열기:
    Eclipse IDE에서 Window > Show View > > Other... > General > Error Log를 선택하여 오류 로그 뷰를 엽니다.

  • 오류 로그 뷰 인터페이스:

    • 오류 로그 뷰는 플러그인의 오류, 경고 및 정보를 표시합니다. 각 로그 항목은 상태 객체의 메시지, 심각도 및 예외 정보를 포함합니다.
    • 더블 클릭하여 자세한 로그 정보를 확인할 수 있습니다.

참고 자료

이클립스 실전 플러그인 개발 (3장: 이클립스 기반 구조), 저자: 에릭 클레이버그

profile
Why not change the code?

0개의 댓글