Как открыть плагин в eclipse
Developing the Eclipse «Hello World» plug-in
Summary
The traditional Hello World program doesn’t do that much, but it can be invaluable when exploring a new development environment. In this article we’ll develop the Eclipse Hello World plug-in and show you how to integrate it with the Eclipse Workbench. After you read this article you should know how to use the Eclipse Java™ Development Tooling to create, run, and debug a simple plug-in that extends the Eclipse Platform. You’ll see how to setup a project for your plug-in, edit the Java code, compile, and run or debug your plug-in in another launched copy of Eclipse. We’ll be looking at plug-in manifest files, extensions, and extension points to see how plug-ins are described to Eclipse and how they are integrated with the Platform.
By Jim Amsden, OTI
Updated September 6, 2002 for Eclipse release 2.0 by Andrew Irvine, OTI
Last revised January 28, 2003
This article describes Eclipse release 2.0, which differs in minor ways from the previous Eclipse release. If you are still working with Eclipse release 1.0, you should consult the original version of this article.
The Eclipse Plug-in Development Environment (PDE) project provides a very nice environment for creating plug-ins and integrating them with the Eclipse Platform and/or other plug-ins. We’re not proposing an alternative to PDE here. PDE is definitely the way to go. But sometimes its helpful to do things «by hand» in order to gain a more thorough understanding of how something works and how the pieces fit together. That’s the approach we’re going to take in this article. We’ll develop a very simple plug-in implementing the Eclipse version of the classic Hello World sample. This will allow us to focus on the relationships between the various components of Eclipse and our plug-in without getting bogged down in the details of the example itself. We’ll go raw as much as possible so we can see what’s actually happening under the covers. Hopefully this will give you a better understanding of Eclipse, help you make better use of PDE, and give you some idea where to look when things don’t go quite as planned.
The Problem
For the Eclipse Hello World program, let’s start out with a simple design. We’re going to add a button to the Workbench toolbar that when pressed, displays an information dialog containing the string «Hello World». Pressing OK dismisses the dialog. Nothing fancy, and certainly not anything that begins to exploit the full extensibility of Eclipse. But something we can easily use to get our feet wet. And it turns out that more complicated examples follow roughly the same pattern so we’ll get a lot out of this very simple example.
So lets get started! I’ll assume you’ve already downloaded the latest Eclipse code drop from eclipse.org, have it installed, and know how to start it up. If not, checkout the downloads page at the Eclipse site, and follow the instructions there.
Step 1: Getting ready to write Java code
Before we get started, we need to check some workbench preferences to make sure its configured properly for plug-in development. We’ll be setting other properties later in the article, but for now, let’s just set the default project layout and select the JRE we’re going to use.
Setting the default project layout : Edit the Java preferences by selecting the Window->Preferences menu item. Expand the Java list item. Click on the New Project item and check the Folders radio button. Ensure » Source folder name » is set to ‘src’ and that » Output folder name » is set to ‘bin’. If you do not alter this setting, all of the class files and source files will be placed in directories based at the root of the project. This is fine for small scale development, but is not the structure you want when constructing bigger programs.
Note: The Hello World plug-in assumes that the project layout has been altered to use ‘src’ and ‘bin’ folders. If you do not do this, you will get runtime errors when testing your plug-in.
Step 2: Creating the plug-in Project
Step 3: Integrating with Eclipse
Before we can start writing our code, we need to determine how we’re going to integrate with Eclipse. That’s because all extensions to Eclipse are done through plug-ins, and plug-ins integrate with each other through extensions on extension points. Eclipse plug-ins typically provide extensions to the platform that support some additional capability or semantics. What is needed is a way for plug-ins to allow other plug-ins to change their behavior in a controlled manner.
Eclipse provides an extensibility mechanism that is scalable, avoids name collisions, doesn’t require compilation of the whole product as a unit, and supports multiple versions of the same component at the same time. Eclipse does this by introducing the notion of a plug-in which encapsulates functional extensions to the Platform. Each plug-in has a name, id, provider name, version, a list of other required plug-ins, and a specification for its runtime. A plug-in can also have any number of extension points that provide a portal into which other plug-ins can add their functionality. This is how Eclipse enables other plug-ins to handle the variability supported by your plug-in. In order to integrate with other plug-ins, a plug-in provides extensions on these extension points (perhaps even its own extension points in order to provide some default behavior).
A plug-in is described in an XML file called the plug-in manifest file. This file is always called plugin.xml, and is always contained in the plug-in sub-directory. The Eclipse Platform reads these manifest files and uses the information to populate and/or update a registry of information that is used to configure the whole platform.
Step 4: Creating the plug-in Manifest File
Now that we know what we’ve got to do, lets tell Eclipse about our plug-in by creating the plug-in manifest file. Use File->New->Other. and select Simple->File to create a file called plugin.xml in the org.eclipse.examples.helloworld project.
Edit the plugin.xml file so that it looks like this:
The plug-in runtime element is how you tell the platform where to find the classes in your plug-in. Essentially, the requires and the runtime elements go together to specify the «classpath» for the plug-in. This approach allows each plug-in to have its own classpath independent of any other plug-in. Further, each plug-in has its own classloader which is used to load all classes defined by that plug-in (i.e., the classes found in its library declarations).
Don’t get confused between an extension-point element and an extension element with a point attribute. The extension-point element defines a hook into the platform while the extension specifies and instance of using the hook. The point attribute is the id of the extension-point you’re extending.
Step 5: Setting up the plug-in Project
Switch to the Java perspective if you’re not already there with Window->Open Perspective->Other and select the Java perspective (or if Java is available on the menu, you may select it directly). Then select the packages explorer tab to view the packages in your project. You should only see the packages you’ve specified in your Java build path so far, probably just the JRE_LIB entry for your rt.jar. Since your going to be using the IWorkbenchWindowActionDelegate interface, and other parts of the platform to develop our example, you need to include the referenced plug-in runtimes in your Java build path.
Now we’re ready to write the code and compile it against workbench.jar and swt.jar. The workbench has the JDK set, the project has all the jar files in its build path we need, and the plug-in manifest file tells us what classes we have to start with.
Step 6: Implementing the IWorkbenchWindowActionDelegate Interface
The code isn’t too complicated, so I won’t cover it in any more detail. Other articles will be addressing the specific Eclipse features we’re using, but we want to focus on plug-ins and the end-to-end integration story rather than the code specifics in this article.
Step 7: Testing your plug-in
We’re ready to test our plug-in! The best way to do this is to use two Eclipse workbenches, one for plug-in development, a second for testing and debug. Let’s call them the development workbench and the testing workbench to avoid confusion. We don’t want to use the same instance the Eclipse workbench for both development and test for a number of reasons:
Prior to launching a testing workbench you must tell Eclipse where the required plugins are located. Edit the Plug-in Development preferences by selecting the Window->Preferences menu item. Expand the Plug-In Development list item. Select Target Platform and click on the Not in Workspace button on the right hand side. You will note the selected items in the list to the left of this button are all now checked. Finally click on the OK button to accept these changes.
Now we can test the Hello World actions. You’ll see the Hello World icon that was specified in the icon attribute of the action element in our plug-in manifest file on the Workbench toolbar (if you don’t provide an icon, it will appear as a gray or red square). Move the cursor over it and you’ll see the hover help you entered. Click and you see the message. Not quite as simple as printf(«Hello World»); but it looks a lot nicer. To turn off the Hello World action set (and remove the associated button), select Window->Customize Perspective. and expand the Other category. Now you can uncheck the Hello World item.
When you turn off the Hello World action set, you’ll see that the icon is removed from the toolbar and you can no longer invoke the plug-in action.
Debugging your plug-in
Conclusion
We’ve seen the complete development of a plug-in extension to Eclipse from design to debug and test. We’ve done all the development, testing, and debugging using Eclipse itself and the Java Development Tooling. The Hello World example doesn’t do much, and the complexity per function is pretty high. But more complex plug-ins follow the same pattern, and Eclipse provides a lot more than we’ve seen with this simple example. You should now have a pretty good idea how all the pieces fit together and what it means to develop and integrate a plug-in. Now its time to do something real. You’ll want to use PDE for that, so look for that article coming soon.
Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.
Microsoft, Windows, Windows NT, and the Windows logo are trademarks of Microsoft Corporation in the United States, other countries, or both.
Еще раз о разработке плагинов Eclipse
Автор: Александр Цимбал
Источник: RSDN Magazine #1-2008
Опубликовано: 17.07.2008
Исправлено: 15.04.2009
Версия текста: 1.0
Поскольку разработка плагинов – главная часть создания приложений для платформы Eclipse, то разработчики самой платформы приложили огромное количество усилий, чтобы сделать этот процесс максимально простым. Эксперты для создания компонентов Eclipse (плагинов (plug-ins) в терминах «классического» Eclipse или бандлов (bundles) в терминах OSGi (Open Services Gateway interface)) с «технической точки зрения» почти тривиальны. Но только при одном условии – если разработчик имеет перед глазами достаточно «общую» картину структуры платформы и имеет отчетливое представление о ее наиболее часто используемых возможностях. Именно создание такого представления занимает б о льшую часть времени, которое должен потратить начинающий разработчик компонентов. Данная статья имеет целью способствовать сокращению этого времени. В ней не ставится задача рассмотреть даже только важнейшие детали – размер статьи не позволит, деталей слишком много. Без знания деталей (например, где располагаются компоненты, какую структуру имеют каталоги плагинов – в «естественном виде» или в виде jar-файлов, какие имена они (каталоги или jar-файлы должны иметь) и многого другого) – плагин не создать. Но излишние детали на определенной стадии знакомства с технологией подобны даже не деревьям, скрывающим лес, а листьям на этих деревьях. Здесь делается попытка вести разговор о лесе – это очень упрощает жизнь при первом знакомстве с новой местностью.
Предполагается, что читатель (на уровне пользователя среды Eclipse) знаком с такими понятиями, как workspace, представление (view), редактор (editor), перспектива (perspective) и пр. Хорошим введением для программистов Eclipse является статья «Проект Eclipse» (http://www.rsdn.ru/article/devtools/eclipse.xml?print).
Структура плагинов и их расположение
Итак, плагин Eclipse.
Как любой компонент, плагин:
Структура среды управления компонентами и самих компонентов вытекают из этих задач.
Как в любой компонентной технологии создания приложений, в Eclipse нужно отличать свойства и настройки самой платформы (развернутого экземпляра Eclipse) и проектов, созданных для выполнения под управлением этой платформы. Поэтому важно понимать, что (в смысле функциональности и свойств) существует на каком уровне и каким образом к этой функциональности нужно получать доступ, причем с учетом различных стадий проектов – разработки, тестирования, отладки, эксплуатации.
В базовом варианте при развертывании платформы Eclipse (и последующем добавлении/удалении плагинов для этого экземпляра платформы) доступные плагины должны находиться в фиксированном (относительно каталога инсталляции) месте, конкретно, в подкаталогах features и plugins. Эти компоненты (в виде подкаталогов или в виде jar-файлов с той же структурой каталогов) потенциально доступны на уровне самой платформы, т.е. всех приложений, выполняемых под данным экземпляром платформы.
Это далеко не всегда удобно и с точки зрения визуального представления среды, и с точки зрения потребных для исполнения приложений ресурсов: чем больше компонентов, тем медленнее запускается платформа, тем больше нужно памяти для загрузки используемых компонентов. В результате обычной является ситуация, когда для разных целей используются несколько различных инсталляций Eclipse, что, в свою очередь, порождает свои сложности (настройка, обновление и т.д.). С помощью довольно изощренного «скриптового программирования» можно «менять настройки» единственного экземпляра платформы (см., например, очень интересную статью http://constantiner.blogspot.com/2006/03/eclipse.html, правда, она отнюдь не для начинающих, но зато в ней много полезных «точек входа» для начала рассмотрения различных тем).
Тем не менее, важно понимать, что б о льшая часть информации о плагинах (их код, XML-дескрипторы, статические ресурсы) «замкнуты» на экземпляр самой платформы Eclipse.
В то же время часть информации о плагине, а именно, их состояние – в том числе настройки (preferences), привязаны не к установленному экземпляру платформы, а к используемому workspace (подкаталог /.metadata/.plugins). Поэтому workspaces имеют отношение не только к прикладным проектам. Впрочем, Eclipse предоставляет возможность «разделения» такой информации между различными workspaces.
Управление компонентами
До появления версии Eclipse 3.1 управление плагинами (регистрация, загрузка и выгрузка, организация связей между ними и пр.) осуществлялось нестандартными средствами, специфическими для Eclipse. На смену этому проприетарному механизму пришло использование открытых стандартов, в данном конкретном случае – OSGi (Open Services Gateway interface). С точки зрения OSGi плагин является «сборкой (bundle)» этого стандарта. Сборка является основной «единицей управления».
Поскольку OSGi не интересует прикладная функциональность компонента, оформленного в виде сборки, то описание сборки выполнено в виде специального XML-документа. Этот документ (файл MANIFEST.MF в подкаталоге META-INF). Содержимое манифеста может выглядеть примерно так:
Термины «Bundle» и «Package» говорят сами за себя – в манифесте объявлены свойства сборок OSGi и (не всегда) пакетов Java, которые должны быть доступны другим сборкам. Bundle-SymbolicName позволяет задать уникальное имя сборки. Name и Vendor – простые текстовые описания.
Тег Require-Bundle перечисляет сборки, которые используются данной сборкой и, соответственно, должны присутствовать при исполнении данного компонента. При работе с дескриптором компонента среда Eclipse (точнее, загрузчик плагинов) определяет наличие всех необходимых сборок, и, если некоторая сборка не найдена, то возбуждается исключение. При этом делается соответствующая запись в журнал (log), а текущая сборка не загружается. Если отсутствие сборки из списка Require-Bundle не является фатальным для загрузки данного компонента, то такую сборку можно пометить как необязательную (resolution:=optional). Среда Eclipse в случае необходимости позволяет разработчику создать и использовать свой собственный загрузчик компонентов.
Важным тегом является тег Eclipse-LazyStart (в Eclipse 3.1 тег назывался Eclipse-AutoStart, который, начиная с Eclipse 3.2, объявлен устаревшим). Значение true говорит о том, что загрузка самого компонента может быть отложена до того момента, когда компонент действительно будет нужен.
О теге Bundle-Activator будет сказано немного ниже.
Манифест является первым XML-дескриптором компонента. Очевидно, что в нем нет ничего, имеющего отношения к функциональности, настройкам компонента, его взаимодействию с другими компонентами и аспектами визуального представления, и многому другому. Эта дополнительная информация находится в другом дескрипторе компонента – файле с именем plugin.xml. Его основное назначение – описать использование «точек расширения» (extension points) Eclipse, о чем разговор впереди.
Работа с дескрипторами компонентов начинается при старте платформы Eclipse. Загрузчик плагинов просматривает каталоги plugins (и/или links) в поисках установленных компонентов, и для каждого компонента анализирует файлы MANIFEST.MF и plugin.xml. При этом в памяти строится структура связей сборок и их описаний (реестр плагинов), но загрузки кода компонентов не происходит (структура управления плагинами занимает в памяти несоизмеримо меньше места, чем «обычный» набор плагинов). Загрузка плагина и его активация (забегая немного вперед – вызов callback-метода start() класса плагина) выполняется тогда, когда это действительно необходимо.
Надо сказать, что управление загрузкой плагинов претерпевало значительные изменения, и связано это, в первую очередь, не с самой платформой Eclipse, а с развитием стандарта OSGi. До использования OSGi, т.е. в Eclipse 2.x, разработчик плагинов в случае, когда необходимо было загрузить плагин до его использования, т.е. при старте самой платформы, должен был использовать специальную точку расширения и реализовывать интерфейс org.eclipse.ui.IStartup. В настоящий момент такой подход использовать не нужно – все необходимые настройки задаются на уровне тега Eclipse-LazyStart (или тега Bundle-ActivationPolicy OSGi). Полнофункциональная поддержка Eclipse-LazyStart и Bundle-ActivationPolicy реализована в Eclipse 3.3. Разработчикам компонентов имеет смысл подробно ознакомиться с возможностями тега Eclipse-LazyStart.
Класс плагина и его использование
Класс плагина (он же класс-активатор плагина) – это класс, экземпляр которого создается средой при загрузке плагина. Этот класс должен реализовывать интерфейс org.osgi.framework.BundleActivator:
Среда вызывает метод start() этого интерфейса сразу после загрузки плагина и метод stop() непосредственно перед его выгрузкой. Полное имя класса плагина обычно указывается в манифесте (MANIFEST.MF), в теге Bundle-Activator.
Класс, реализующий этот интерфейс, должен обязательно иметь конструктор по умолчанию (т.е. без аргументов), который используется платформой для создания экземпляра этого класса (с помощью вызова метода getInstance()).
На практике класс плагина (точнее, сам плагин – не обязательно класс его активатора) должен уметь гораздо больше, чем выполнять некоторые действия при инициализации (активизации) и деинициализации, поэтому платформа Eclipse предлагает в готовом виде небольшую иерархию классов, которые реализуют данный интерфейс. «Корнем» этой иерархии является класс org.eclipse.core.runtime.Plugin, и именно на основе этого класса разработчик создает класс плагина (в первую очередь это касается плагинов, не имеющих визуального графического представления):
Помимо конструктора и методов управления циклом жизни, класс Plugin содержит несколько очень полезных методов:
Если же разработчик собирается создать плагин с графическим интефейсом, то обычно используется другой класс – AbstractUIPlugin:
Основным отличием этого класса от класса Plugin является поддержка дополнительных хранилищ и реестров, в той числе для графической информации, а также автоматическое сохранение настроек плагине при ее остановке.
Запущенный экземпляр платформы Eclipse выполняется под управлением одной виртуальной машины Java (JVM). Что касается компонентов, то для загрузки каждого плагина используется свой загрузчик классов (classLoader). Такое архитектурное решение приводит к тому, что для использования некоторых «внешних» библиотек, которые должны иметь доступ к ресурсам плагина (например, log4j), необходимо обеспечить специальный механизм загрузки классов. Этот механизм называется «buddy loading», и управление им осуществляется с помощью задания специальных тегов манифеста плагина – Eclipse-BuddyPolicy и Eclipse-RegisterBuddy (реализовано начиная с Eclipse 3.3).
Класс платформы Eclipse
Поскольку управление компонентами берет на себя среда, то разработчику плагина часто нужно получить информацию о параметрах настройки и исполнения самой среды. Такой информацией может быть список установленных плагинов, каталоге установке, менеджере заданий (job manager, задания – объекты Eclipse, являющиеся, по сути, высокоуровневыми аналогами потоков Java), связях между плагинами и многое другое. Для этого су ществует специальный класс, содержащий большое количество полезных сервисных функций – org.eclipse.core.runtime.Platform. Как и следовало ожидать, все методы и поля этого класса являются статическими.
Дескриптор plugin.xml плагина
Второй основной дескриптор компонента (наряду с манифестом) – xml-дескриптор, который должен находиться в файле plugin.xml. Этот дескрптор также анализируется загрузчиком плагинов Eclipse до того, как будет загружаться и исполняться код плагина. Основная задача этого дескриптора – описать связи плагинов, которые создаются с помощью механизма «точек расширения». Точки расширения кратко будут рассмотрены ниже, здесь же пока нужно запомнить следующее: использование данных дескрипторов возможно без загрузки кода (и других ресурсов) плагина. Оказывается, что под эту категорию попадают не только явно «статические» связи между компонентами, но и информация, которая в программировании обычно используется на уровне кода компонентов (а не их дескрипторов).
На практике разработчик довольно редко работает с дескрипторами компонента «вручную» – PDE содержит графические эксперты, которые существенно облегчают работу и снижают вероятность ошибок.
Впрочем, понимание структуры и назначения дескриптора plugin.xml появится только после знакомства с основами «точек расширения» Eclipse.
Точки расширения (extension points)
Тема точек расширения – ключевая для создания расширений платформы Eclipse. Использование или создание точки расширения, с одной стороны, простая задача, так как ее решение четко детерминировано и автоматизировано. С другой стороны, создание универсального компонента для «широкого использования», который, к тому же, эффективно использует вычислительные ресурсы – непростая задача при использовании какой угодно компонентной технологии.
Работа с точками расширения отчетливо делится на две подобласти: использование существующих точек и создание собственных. Первое с технической точки зрения не представляет особых сложностей. Вторая задача намного сложнее.
Для понимания механизма точек расширения лучше начать с обзора тех задач, которые возникают при создании собственной точки расширения, и только потом перейти к созданию расширения для существующей точки (хотя прикладные программисты намного чаще используют точки расширения, чем создают их). Примеры простого использования точек расширения будут приведены по ходу изложения материала.
Создание точки расширения
Смысл механизма точек расширения состоит в возможности динамического и слабосвязанного расширения функциональности любых плагинов Eclipse. В принципе, это довольно похоже на стандартные возможности объектно-ориентрированного программирования – в смысле как наследования классов, так и реализации интерфейсов.
Возьмем, к примеру, представления Eclipse (связанные с перспективой, но это не так уж важно на этом этапе). Есть, условно говоря, «список представлений», а также код, который этими представлениями управляет, причем вполне единообразно. Это немного похоже на контейнеры стандартных библиотек Java или C++ (конечно, контейнеры не связаны с визуальным представлением информации, но это в данном случае непринципиально). При этом различные представления являются компонентами «различных типов», хотя и сводимых к общему типу «представление».
Напрашивается стандартный объектно-ориентированный подход, а именно: создать «базовый класс представления» со стандартной базовой функциональностью, предоставить возможность программисту создавать производные от него классы и переопределять виртуальные методы (это делают сами объектно-ориентированные языки программирования), а также создать класс для управления некоторой совокупностью экземпляров абстрактных «представлений» (на практике – ссылок на экземпляры реальных классов, непосредственно или опосредованно производных от «базового» представления). Ниже приведен псевдо-код для иллюстрации:
Все превосходно, но если рассматривать данный подход как универсальное решение, возникает ряд проблем:
Для решения этих и других проблем и используется механизм точек расширения Eclipse.
Создатель точки расширения должен сделать следующее:
При создании точек расширения нужно принимать во внимание многие обстоятельства.
В первую очередь, нужно избегать наличия в коде данного плагина «фиксации» информации, касающейся будущих расширений. Эту информацию нужно не «зашивать» в код, а помещать в дескрипторы плагина(ов), что приводит к необходимости тщательного продумывания структуры создаваемой точки расширения.
Это означает, что фрагмент XML-дескриптора плагина имеет определенную структуру, а для того чтобы с ним могли работать эксперты среды, нужно задать схему этого документа. Описание точки расширения содержит иерархию элементов и их атрибутов. Состав и назначение элементов определяется функциональностью создаваемой точки расширения.
Во-вторых, нужно всегда помнить, что для одной точки расширения может существовать множество различных реализаций этого расширения, созданных разными авторами. На практике это приводит к использованию различного рода контейнеров ссылок и/или механизма listener’ов. Читателю, который не знаком с механизмом listener’ов, реализованного, например, в рамках технологии JavaBeans, необходимо ознакомиться с этим шаблоном организации взаимодействия компонентов.
В-третьих, нужно полагать, что расширения представляют собой плагины Eclipse, т.е. для их загрузки используются различные загрузчики классов, что не позволяет полагаться на механизм создания экземпляров с помощью вызова Class.forName(). Для создания экземпляра классов (и загрузки, если необходимо, плагина) нужно использовать специальные методы – IConfigurationElement.createExecutable() или IConfigurationElement.createExecutableExtension(). Типичный код может выглядеть примерно так:
В-четвертых, нужно помнить о потокобезопасности кода, в том числе и о том, чтобы не использовать средства защиты на уровне потоков тогда, когда в этом нет необходимости (например, при работе с SWT обращения к компонентам (визуальным сущностям) выполняются всегда из одного потока – UI-потока).
В-пятых, нужно всегда помнить об обработке исключительных ситуаций, особенно с учетом того, что код плагинов выполняется под управлением среды Eclipse. Для вызова методов плагинов, связанных с возможным возникновением исключений, очень удобно использовать специальный интерфейс org.eclipse.core.runtime.ISafeRunnable:
Вызов метода run() реализации этого интерфейса часто выполняется с помощью вызова
При возникновении исключения выполняется код callback-метода handleException().
Использование точки расширения
Действия при использовании существующей точки расширения диктуются особенностями структуры этой точки. Описание грамотно спроектированной и созданной точки содержит (в теге description) описание правил ее использования. Тем не менее, есть действия, общие при использовании всех точек.
Плагин, использующий точку (или точки) расширения (другими словами, реализующий расширение) должен зарегистрировать эту реализацию в среде, предоставив исполняющей среде Eclipse (в первую очередь, загрузчику плагинов) всю необходимую информацию. Какая именно информация является необходимой (помимо идентификатора используемой точки расширения и идентификатора самого создаваемого расширения), определяется создателем точки.
По сути, создание расширения заключается в реализации callback-методов, которые будут вызываться теми плагинами, которые и определили данную точку. Поскольку точки расширения «ведут себя» по-разному, удобно совместить знакомство с их использованием с рассмотрением некоторых стандартных точек расширения Eclipse – теми, которые наиболее часто используются прикладными программистами.
Некоторые стандартные точки расширения
Общий список стандартных точек расширения Eclipse можно получить различными способами. Можно обратиться к встроенной справочной системе (Help->Help Contents->Platform Plug-in Developer Guide->Reference->Extension Point Reference. Другой способ получения той же информации, уже на стадии разработки плагина, – использование эксперта создание плагинов (закладка extensions эксперта, кнопка Add. ).
Рисунок 1. Выбор точки расширения.
К сожалению (или к счастью?), стандартных точек расширения очень много, и начинающему разработчику плагинов трудно выбрать наиболее часто используемые. Ниже мы рассмотрим, на какие точки следует обратить внимание в первую очередь и как их нужно использовать.
Стандартные точки расширения Eclipse разбиты на несколько групп. Наиболее важной (на начальной стадии разработки плагинов) являются точки группы Workbench – их (но не только их) идентификаторы начинаются с org.eclipse.ui. Начать проще всего с «действий» (Actions).
Действия (Actions)
Действия Eclipse тесно связаны с механизмом доступа к этим действиям, но их не следует смешивать. Action – отдельная сущность, не тождественная пунктам меню или кнопкам панелей инструментов. Одно и тоже действие может быть сделано доступным различными способами.
Действие Eclipse реализуется с помощью нескольких классов и интерфейсов. Сделано это вследствие того, что функциональность этого объекта явно делится на визуальную («интерфейс пользователя») и «неотображаемую» часть. Такое разделение интерфейса (задаваемого на уровне дескриптора плагина) и собственно кода позволяет отображать «средства доступа» к действию (меню, кнопки) без необходимости загрузки кода плагина, реализующего необходимые операции. Кроме того, на основе информации в дескрипторе можно управлять видимостью и доступностью действий.
Основой действия (как элемента Eclipse) является интерфейс org.eclipse.jface.action.IAction и реализующий его абстрактный класс Action.
Программист же, создавая плагин, использует другой интерфейс (точнее, один из нескольких интерфейсов), который отвечает за «клиентскую» функциональность. Основным из этих клиентских интерфейсов является интерфейс org.eclipse.ui.IActionDelegate:
Метод run() должен содержать выполняемый код для действия. Он вызывается при нажатии на соответствующую кнопку или при выборе пункта меню (если действие доступно). Метод selectionChanged() сигнализирует о том, что при интерактивной работе в среде Eclipse произошло изменение выбранного элемента интерфейса пользователя. Вместо непосредственной реализации этого интерфейса разработчик плагина может использовать (в качестве базового класса) абстрактный класс org.eclipse.ui.actions.ActionDelegate.
На практике при создании расширений обычно используется не IActionDelegate, а производные от него интерфейсы: IWorkbenchWindowActionDelegate (при работе с главным меню и панелью кнопок рабочей среды (workbench)), IObjectActionDelegate (при работе с контекстным меню), IViewActionDelegate (при работе с представлениями, view) и IEditorActionDelegate (при работе с редакторами, editor). Эти производные интерфейсы полнее используют специфику выполнения действий в том или ином контексте.
Обычная задача программиста – создать класс, реализующий один из этих интерфейсов, объявить его в дескрипторе плагина и сопоставить эту реализацию с кнопками и/или пунктами меню. Продемонстрируем это на примере действия на уровне главного окна среды Eclipse (т.е. с реализацией IWorkbenchActionDelegation)
Действие Eclipse задается с помощью довольно большого набора свойств (элементов и атрибутов, в терминах структуры точки расширения). Для его задания используется тег :
Смысл атрибутов понятен: class задает имя класса, реализующего интерфейс IWorkbenchActionDelegate, icon – используемую графическую пиктограмму, id – уникальный идентификатор действия (он нужен для связи с объектом IAction), label – надпись, сопоставленная с элементом доступа к действию (элемент меню или кнопка панели инструментов), tooltip – текстовая подсказка. Атрибуты menubarPath и toolbarPath определяют место в меню и панели инструментов, где появится соответствующий элемент управления. Обратите внимание, что меню пока не определено.
Реализация интерфейса IWorkbenchActionDelegate может быть следующей:
Следующий шаг – привязка данного действия к элементу меню. Связка «action – menu» является частью тега :
Здесь не место подробно обсуждать особенности свойств меню – смысл ясен и так.
Ну, и, наконец, привязка actionSet’а к точке расширения. Эта точка расширения, как сейчас уже можно догадаться, называется org.eclipse.ui.actionSets:
Вот и все, что нужно сделать для реализации расширения для стандартной точки org.eclipse.ui.actionSets. Эксперты PDE позволяют полностью отказаться от работы «вручную» с дескриптором плагина – файлом plugin.xml.
При работе с контекстным меню используется примерно тот же подход, но будет задействована другая точка расширения, а реализовать нужно немного другой интерфейс – не IWorkbecnchActionDelegate, а IObjectActionDelegate:
Наличие метода setActivePart() позволяет получить (и сохранить) ссылку на активное в настоящее время представление или редактор (Part – общий термин Eclipse для обозначения «окон» в составе перспективы). Следовательно, реализация этого метода в классе для данного действия может выглядеть так:
Теперь осталось сопоставить эту реализацию действия с элементом контекстного меню и с точкой расширения org.eclipse.ui.popupMenus:
При работе с точкой popupMenus нет смысла говорить о панели инструментов, зато важно учесть, что контекстное меню при нажатии пользователем правой кнопки мыши должно появляться не всегда, а только при определенных условиях (например, в зависимости от типа выбранного элемента пользовательского интерфейса, числа выбранных элементов и многого другого). Кроме того, контекстное меню часто создается не «статически» (точнее, не только статически), на уровне дескриптора плагина – это потребовало бы реализации весьма изощренной логики с использованием XML – но и «динамически» (при работе программы).
В данном примере на параметры отображения контекстного меню влияют два атрибута:
К теме действий относятся также реализация точeк расширения (связанных с поддержкой Actions) отдельно для представлений и редакторов Eclipse. Специфика в данном случае состоит в том, что эти панели интерфейса могут иметь, наряду с контекстным меню, еще и панели инструментов, и выпадающее (pull-down) меню. Откройте, например, перспективу Java Browsing и посмотрите на представление Members.
Для версии 3.3 идентификаторы можно найти в дескрипторе plugin.xml (в jar-файле для org.eclipse.jdt.ui, в моей версии Eclipse это файл org.eclipse.jdt.ui_3.3.1. r331_v20070906.jar). В этом файле информация об идентификаторах стандартных элементов интерфейса представлена в следующем виде:
Теперь можно вернуться к меню для представлений. Главной особенностью при реализации точки расширения viewActions является реализация интерфейса org.eclipse.ui.IViewActionDelegate:
Метод init() обычно используется для сохранения передаваемого ему значения «рабочего» представления:
Для задания кнопок на панели инструментов представления и выпадающего меню точки расширения определены элементы и – подобно тому, как это делалось для точки расширения actionSets.
При создании расширения для редактора, а не представления, нужно использовать интерфейс IEditorActionDelegate:
Класс, реализующий этот интерфейс, похож на класс, реализующий интерфейс IObjectActionDelegation.
Создание и добавление перспектив и представлений
Создание таких «укрупненных» элементов пользовательского представления – задача несложная. Сложной задачей является наполнение этих панелей и поддержка работы с ними в стиле, характерном не для тестов, а для законченных программных продуктов.
Для создания перспектив предусмотрена точка расширения org.eclipse.ui.perspectives. Структура ее описания в plugin.xml очень проста:
Здесь мы впервые столкнулись с довольно обычной ситуацией, когда класс расширения, объявленный в дескрипторе плагина, является не реализацией готового объекта, а, по сути, фабрикой нужных объектов. Класс MyPerspectiveFactory должен реализовывать интерфейс org.eclipse.ui.IPerspectiveFactory:
Перспектива Eclipse, по сути, представляет собой контейнер для «дочерних» элементов интерфейса – представлений, редакторов, панелей, меню. Ее задача (с точки зрения создания визуального представления) – задать начальную компоновку (Layout) для управления расположением панелей, задать начальное состояние элементов управления и т.д. После того, как перспектива создана и получила начальную компоновку, созданный экземпляр фабрики перспектив больше не нужен.
Для задания начального состояния перспективы программист реализует метод createInitialLayout(). Eclipse включает в себя эксперт, который генерирует «демонстрационную» перспективу как образец для создания перспективы собственной. Код метода createInitialLayout() для компоновки стандартных представлений может выглядеть примерно так:
Если нужно не создавать перспективу заново, а «статически», на уровне дескрипторов плагина, изменить существующую, нужно использовать точку расширения org.eclipse.ui.perspectiveExtensions. По сути, создание расширения для этой точки заключается в формировании перспективы на уровне заданий XML-описаний.
Создание представления, напротив, обычно выполняется в виде написания производного класса на базе стандартного класса org.eclipse.ui.part.ViewPart (программист может просто реализовать интерфейс org.eclipse.ui.IViewPart, но, как правило, проще наследовать готовый класс).
Рисунок 2.
Элементы, отображаемые как папки, являются категориями. На данном рисунке категории содержат только представления, но не подкатегории, хотя точка расширения позволяет создавать древовидную структуру категорий (с помощью атрибута parentCategory).
Элементы и находятся на одном уровне, и привязка представления к конкретной категории выполняется за счет задания идентификаторов:
Что касается кода создаваемого класса, то начать можно с примера, который генерирует эксперт Eclipse. Для красивого отображения информации в представлениях нужно довольно подробно ознакомиться с использованием SWT (или его аналогов), а также с очень мощными и полезными средствами JFace.
Другие часто используемые точки расширений
Здесь очень кратко будут рассмотрены (точнее сказать – упомянуты) те точки расширения, на которые стоит обратить внимание в первую очередь.
Точка для создания приложений.
Точки для создания команд и привязки их действиям
В ранних версиях Eclipse клавиатурные акселераторы вызовов действий были частью описания действий. Теперь команды задаются отдельно, действия – отдельно, привязка команд к действиям – отдельно. Это позволяет избежать ошибок и повысить гибкость средств настройки.
Точки расширения – org.eclipse.ui.commands (задание команд, по категориям – примерно так же, как это сделано с представлениями) и org.eclipse.ui.bindings (задание сочетаний клавиш и привязка к команде по ее идентификатору). Действие (action) сопоставляется с командой (по ее идентификатору) с помощью указания идентификатора команды в атрибуте definitionId действия.
Заключение
Понимание возможностей стандартных плагинов Eclipse, т.е. грамотное использование точек расширения, вместе с умением создавать свои собственные точки, предоставляют программистам совершенно исключительные возможности по созданию любых графических приложений с использованием языка Java.
