The "One Laptop Per Child" project has a great device ready to ship, but there's no Java on there. Let's think about working together to put Java on OLPC!
If you're unfamiliar with extension points, check out
the previous installment
in the series first. Here, we'll assume basic knowledge about extension points and how to iterate over them; we're going to cover how to create a new extension point for others to contribute towards. Also, if you've been following the
Getting started with OSGi
series, then you might have some questions about the differences between extension points and OSGi services; if so,
Neil's article
on EclipseZone should answer your questions.
So, let's get started. An extension point is a hook that allows two plug-ins to communicate through the exchange of data or code. All extension points are defined in XML; these are then stored in the Extension Registry and accessed on demand from client plug-ins. One nice feature of Eclipse's Extension Registry is that it is possible to dynamically link plug-ins together at runtime but prior to the bundle having been started. The Extension Registry is also cached, so that the second time you start up Eclipse, it starts faster than it did previously.
As an example, we're going to create a plug-in
com.example.pizza
, which will define an extension point called
com.example.pizza
allowing others to define new pizza topping types, each of which will have a name and a cost associated with them. Later, we'll create a second plug-in to contribute to the first. We'll base the plug-in on the Hello World template
as last time
so that we have a handy button we can click to run some code.
To define an extension point, we need two pieces of information:
The id and name of the extension point
A pointer to an XML Schema that defines what valid XML content is allowed
These are defined in a
plugin.xml
file, looking something like this:
Having defined this extension point in a file (and the
associated schema
; also see screenshot of schema editor) we can use it in this or other plug-ins. The content of the
pizza
element looks like:
Putting this together into a plug-in (see the
com.example.pizza_1.0.0.jar
attachment) means that we can iterate over all the toppings and display them:
publicvoid run(IAction action) {
StringBuffer buffer = new StringBuffer();
IExtensionRegistry reg = Platform.getExtensionRegistry();
IConfigurationElement[] extensions = reg
.getConfigurationElementsFor("com.example.pizza");
for (int i = 0; i < extensions.length; i++) {
IConfigurationElement element = extensions[i];
buffer.append(element.getAttribute("topping"));
buffer.append(" (");
String cost = "unknown";
if (element.getAttribute("cost") != null) {
cost = element.getAttribute("cost");
}
buffer.append(cost);
buffer.append("\n");
}
MessageDialog.openInformation(window.getShell(),
"Installed pizza toppings", buffer.toString());
}
One of the reasons to do this in an action (or other menu-driven interrupt) as opposed to the
Plugin
's
start
method in the bundle activator: you can't guarantee when the bundles will be installed/resolved. Bundle/service dependency and startup order is outside the scope of this article; but suffice to say, by the time the UI comes up, all the bundles have been sufficiently resolved/started to enable the extension points to be registered.
So, we can register our own extension point, and get textual data from that code. What about trying to do something useful, like using it to instantiate a class?
Let's create a secondary plug-in,
com.example.pizza.ham_1.0.0.jar
. We'll create a class called
HamTopping
, and an associated interface
ITopping
in the
com.example.pizza
plug-in. We can then change the extension point to instantiate the class instead using of a hard-coded cost, and do something dynamically:
This might seem right in principle, but if you try and run it you'll get a
ClassNotFoundException
, because
com.example.pizza.ham
depends on
com.example.pizza
, not the other way around; and thus the classes aren't visible. (It does work when the extension is in the same package, but shouldn't really be relied upon.)
So what are we to do? Well, there's two choices;
Use OSGi's
Dynamic-ImportPackage
to extend the scope of the ClassLoader
Use the Extension Registry's
createExecutableExtension()
to do the resolution instead
The standard way is to leverage Extension Registry. To do this, you'll need to change the
getAttribute()
and associated
Class.forName().newInstance()
methods; which has the side effect of being slightly cleaner from an exception perspective, too:
You can do this by editing the schema in PDE's schema editor (see attached screenshot of the pizzaschema), and for the
class
attribute, selecting
java
as the type, and placing
com.example.pizza.ITopping
as the interface to implement. (Note that you can also base it on a derived class instead of an interface should you desire.)
Having changed both the schema and the plug-in to invoke
createExecutableSchema()
, we can now launch our Pizza window to iterate through all the installed toppings, including the dynamic
ITopping
class that is resolved from another package. If you want to explore, feel free to create more plugins and insert them into the Eclipse runtime, and you'll see the additional toppings traversed each time you click on the button. The adventurous amongst you might even try starting up the Eclipse runtime with
-console
, and verify that uninstalling the toppings ensures they are no longer displayed. (Note that it's not enough to stop the bundle; extensions are still present even when a bundle is stopped. The bundle needs to be uninstalled for the extension registry to forget about it).
Hopefully this has given you an idea about how to create your own extension points, and a bit more appreciation of why Eclipse uses this pattern for all of its extendability. If you've got any questions, feel free to leave them below.
Re: Getting started with Eclipse plug-ins: creating extension points
Good article, very clear and to the point, thanks.
One thing I'm wondering, is there a standard extension point that defines OSGi Services?
I've read your other article on declarative services, that's cool, but it yields yet another xml file to put in, and there is no tool assist to build the file.
Even though it would mix programming models, it could be nice to have a declarative way to define OSGi services as plugin extension points too, IMHO.
Re: Getting started with Eclipse plug-ins: creating extension points
Thanks for the kind words, but it was actually Neil who wrote the stuff on Declarative Services.
As far as I know, there isn't an editor for the OSGi services entries, although there's no reason why there couldn't be. (Neil, Peter; are you going to write one?)
As for declaring an extension point for registering OSGi services; that's pretty much what Declarative Services is supposed to do That being said, there's no reason why you couldn't use the concepts in earlier articles on scanning the extension registry and Neil's articles on registering a service to write a bundle that does that.
Re: Getting started with Eclipse plug-ins: creating extension points
Thanks for your valuable guidance..But after completing step 24 ..and if run the com.example.pizza as an eclipse application.It is opening another Eclipse window (i mean another eclipse editor)..iam not getting the output window... Pls tell me where i would have gone wrong.. Waiting for your reply with hopes..
Thanks for your valuable guidance..But after completing step 24 ..and if run the com.example.pizza as an eclipse application.It is opening another Eclipse window (i mean another eclipse editor)..iam not getting the output window... Pls tell me where i would have gone wrong.. Waiting for your reply with hopes..
Re: Getting started with Eclipse plug-ins: creating extension points
Hi,
I have a problem - getConfigurationElements() is always returning an empty array.
I even tried,
IExtension[] extensions = extensionPoint.getExtensions();
but still the array is empty.
I downloaded a sample project and debugged that to see could be wrong in my plug-in. I found that, the extended plug-ins get listed under
objectManager --> namespacesIndex
hashtable(downloaded project).
In my project the extended plug-ins get listed under
objectManager --> extensionPoints
hashtable.
Any guess, what could be the problem?
If you need any particular info I shall provide...
Re: Getting started with Eclipse plug-ins: creating extension points
I was able to create the com.example.pizza plugin and see the dialog showing the "cheese (1)". I created the second plugin, com.example.pizza.ham, but I'm not able to see the additional toppings.
I ran with the -console argument and here is a subset of the plugins. As you can see, the ham plugin is resolved. I've also ran with the -consoleLog to verify there were no errors. Any idea what the problem may be?
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.1.R33x_v20070828
1 ACTIVE org.eclipse.equinox.common_3.3.0.v20070426
2 ACTIVE org.eclipse.update.configurator_3.2.101.R33x_v20070810
3 ACTIVE org.eclipse.core.runtime_3.3.100.v20070530
4 RESOLVED com.example.pizza.ham_1.0.0
5
> com.example.pizza_1.0.0
6 RESOLVED com.ibm.icu.source_3.6.1.v20070906
Re: Getting started with Eclipse plug-ins: creating extension points
Try deleting the launch configuration and re-creating a new one. Sometimes the PDE cache stores out-of-date information with respect to the extension points.
Re: Getting started with Eclipse plug-ins: creating extension points
Try deleting the launch configuration and re-creating a new one. Sometimes the PDE cache stores out-of-date information with respect to the extension points.
Getting started with Eclipse plug-ins: creating extension points
At 7:08 PM on Jun 19, 2007, Alex Blewitt
wrote:
If you're unfamiliar with extension points, check out the previous installment in the series first. Here, we'll assume basic knowledge about extension points and how to iterate over them; we're going to cover how to create a new extension point for others to contribute towards. Also, if you've been following the Getting started with OSGi series, then you might have some questions about the differences between extension points and OSGi services; if so, Neil's article on EclipseZone should answer your questions.
So, let's get started. An extension point is a hook that allows two plug-ins to communicate through the exchange of data or code. All extension points are defined in XML; these are then stored in the Extension Registry and accessed on demand from client plug-ins. One nice feature of Eclipse's Extension Registry is that it is possible to dynamically link plug-ins together at runtime but prior to the bundle having been started. The Extension Registry is also cached, so that the second time you start up Eclipse, it starts faster than it did previously.
As an example, we're going to create a plug-in
com.example.pizza, which will define an extension point calledcom.example.pizzaallowing others to define new pizza topping types, each of which will have a name and a cost associated with them. Later, we'll create a second plug-in to contribute to the first. We'll base the plug-in on the Hello World template as last time so that we have a handy button we can click to run some code.To define an extension point, we need two pieces of information:
These are defined in a
plugin.xmlfile, looking something like this:Having defined this extension point in a file (and the associated schema ; also see screenshot of schema editor) we can use it in this or other plug-ins. The content of the
pizzaelement looks like:Putting this together into a plug-in (see the
com.example.pizza_1.0.0.jarattachment) means that we can iterate over all the toppings and display them:public void run(IAction action) { StringBuffer buffer = new StringBuffer(); IExtensionRegistry reg = Platform.getExtensionRegistry(); IConfigurationElement[] extensions = reg .getConfigurationElementsFor("com.example.pizza"); for (int i = 0; i < extensions.length; i++) { IConfigurationElement element = extensions[i]; buffer.append(element.getAttribute("topping")); buffer.append(" ("); String cost = "unknown"; if (element.getAttribute("cost") != null) { cost = element.getAttribute("cost"); } buffer.append(cost); buffer.append("\n"); } MessageDialog.openInformation(window.getShell(), "Installed pizza toppings", buffer.toString()); }So, we can register our own extension point, and get textual data from that code. What about trying to do something useful, like using it to instantiate a class?
Let's create a secondary plug-in,
com.example.pizza.ham_1.0.0.jar. We'll create a class calledHamTopping, and an associated interfaceIToppingin thecom.example.pizzaplug-in. We can then change the extension point to instantiate the class instead using of a hard-coded cost, and do something dynamically:public class HamTopping implements ITopping { public int getCost() { return (int)(Math.random()*10); } public String toString() { return "Ham topping"; } }Now, when traversing the list of installed pizza toppings, instead of looking for the cost by number, we'll do a resolution:
if (element.getAttribute("code") != null) { cost = element.getAttribute("cost"); } else { try { ITopping topping = (ITopping) Class.forName( element.getAttribute("class")).newInstance(); cost = String.valueOf(topping.getCost()); } catch (Exception e) { cost = e.toString(); // obviously for demo purposes only } }This might seem right in principle, but if you try and run it you'll get a
ClassNotFoundException, becausecom.example.pizza.hamdepends oncom.example.pizza, not the other way around; and thus the classes aren't visible. (It does work when the extension is in the same package, but shouldn't really be relied upon.)So what are we to do? Well, there's two choices;
Dynamic-ImportPackageto extend the scope of the ClassLoadercreateExecutableExtension()to do the resolution insteadThe standard way is to leverage Extension Registry. To do this, you'll need to change the
getAttribute()and associatedClass.forName().newInstance()methods; which has the side effect of being slightly cleaner from an exception perspective, too:if (element.getAttribute("code") != null) { cost = element.getAttribute("cost"); } else { try { ITopping topping = (ITopping) element.createExecutableExtension("class"); cost = String.valueOf(topping.getCost()); } catch (CoreException e) { cost = e.toString(); // obviously for demo purposes only } }For this to work properly for PDE, the attribute name 'class' needs to be decorated with extra information in the schema:
You can do this by editing the schema in PDE's schema editor (see attached screenshot of the pizzaschema), and for the
classattribute, selectingjavaas the type, and placingcom.example.pizza.IToppingas the interface to implement. (Note that you can also base it on a derived class instead of an interface should you desire.)Having changed both the schema and the plug-in to invoke
createExecutableSchema(), we can now launch our Pizza window to iterate through all the installed toppings, including the dynamicIToppingclass that is resolved from another package. If you want to explore, feel free to create more plugins and insert them into the Eclipse runtime, and you'll see the additional toppings traversed each time you click on the button. The adventurous amongst you might even try starting up the Eclipse runtime with-console, and verify that uninstalling the toppings ensures they are no longer displayed. (Note that it's not enough to stop the bundle; extensions are still present even when a bundle is stopped. The bundle needs to be uninstalled for the extension registry to forget about it).Hopefully this has given you an idea about how to create your own extension points, and a bit more appreciation of why Eclipse uses this pattern for all of its extendability. If you've got any questions, feel free to leave them below.
11 replies so far (
Post your own)
Re: Getting started with Eclipse plug-ins: creating extension points
Good article, very clear and to the point, thanks.One thing I'm wondering, is there a standard extension point that defines OSGi Services?
I've read your other article on declarative services, that's cool, but it yields yet another xml file to put in, and there is no tool assist to build the file.
Even though it would mix programming models, it could be nice to have a declarative way to define OSGi services as plugin extension points too, IMHO.
Regards,
gregware
Re: Getting started with Eclipse plug-ins: creating extension points
Thanks for the kind words, but it was actually Neil who wrote the stuff on Declarative Services.As far as I know, there isn't an editor for the OSGi services entries, although there's no reason why there couldn't be. (Neil, Peter; are you going to write one?)
As for declaring an extension point for registering OSGi services; that's pretty much what Declarative Services is supposed to do
Alex.
Re: Getting started with Eclipse plug-ins: creating extension points
Very nice articleProved very useful!!!!!
Thanks
Vipul
Re: Getting started with Eclipse plug-ins: creating extension points
I've posted the next installment in this series.Re: Getting started with Eclipse plug-ins: creating extension points
Creating Extension Points 1 - details instructions Eclipse 3.31)File; New; Project; Plug-in Project; Next;
2)Project name: com.example.pizza; Next; Next;
3)Templates; Available templates: Hello, World; Finish
4)DC. MANIFEST.MF; Extension Points; All Extension Points; Add
5)Extention Point ID: com.example.pizza
6)Extention Point Name: Pizza toppings
7)Finish
8)RC. file com.example.pizza.exsd; Open With; NotePad
9)Edit; Select All; Edit; Copy
10)Select in ?Extension Point Schema Editor? Tab: com.example.pizza.exsd
11)Edit; Select All; Edit; Paste; File; Save
12)Close Shema - window ?
13)DC. MANIFEST.MF; Extensions; Add
14)Extension Point selection; Extention Point; Extension Point filter: com.example.pizza; Finish
15)in extension pizza:
topping: cheese
cost: 1
16)File; Save
17)In package: com.example.pizza.actions; class: SampleActions.java;
method: run( ) change:
with:
public void run(IAction action) { StringBuffer buffer = new StringBuffer(); IExtensionRegistry reg = Platform.getExtensionRegistry(); IConfigurationElement[] extensions = reg.getConfigurationElementsFor("com.example.pizza"); for (int i = 0; i < extensions.length; i++) { IConfigurationElement element = extensions[i]; buffer.append(element.getAttribute("topping")); buffer.append(" ("); String cost = "unknown"; if (element.getAttribute("cost") != null) { cost = element.getAttribute("cost"); } buffer.append(cost); buffer.append(" )"); buffer.append("\n"); } MessageDialog.openInformation(window.getShell(), "Installed pizza toppings", buffer.toString()); }18)RC. Project com.example.pizza; Run as; Eclipse Aplication;
19)Close runtime Welcome screen; Press button: : Hello, Eclipse world
20)You will see dialog box: Installed pizza toppings: cheese(1); Yes
Creating Extension Points 2 - details instructions Eclipse 3.3
1)Open project com.example.pizza
2)DC. schema/com.example.pizza.exsd; Open Whit; Extension Point Schema Editor
3)Select Tab: com.example.pizza.exsd; Mention atribute:
<attribute name="class" type="string">
<annotation>
<appInfo>
<meta.attribute kind="java" basedOn="com.example.pizza.ITopping"/>
</appInfo>
</annotation>
</attribute>
4)Close windowwith Shema: com.example.pizza.exsd
5Copy:
package com.example.pizza; public interface ITopping { public int getCost(); }6)RC. Project com.example.pizza; Paste
7)RC. MANIFEST.MF; Open Whith; Plug-in Manifest Editor; Runtime; Exported Packages; Add
8)Select packages to export: com.example.pizza; OK
9)File; Save;
10)Close MANIFEST window (window com.example.pizza) and window Itopping.java
11)File; New; Project; Plug-in Project; Next;
12)Project name: com.example.pizza.ham; Next
13)Generate an activator?.: uncheck; Finish
14)Copy:
package com.example.pizza.ham; import com.example.pizza.ITopping; public class HamTopping implements ITopping { public int getCost() { return 2; } public String toString() { return "Ham topping"; } }15)RC. Project com.example.pizza.ham; Paste
16)DC. MANIFEST.MF; Dependencies; Required Plug-ins; Add
17)Select a Plug-in: com.example.pizza; OK; File; Save
18)Select: Extension; Add
19)Extension Point selection; Extention Point; Extension Point filter: com.example.pizza; Finish
20)in extension pizza:
topping: ham
class: com.example.pizza.ham.HamTopping (Use Browse)
21)RC. com.example.pizza; New; pizza
topping: pepperoni
cost: 1
22)File; Save; Close MANIFEST window and window HamTopping.java
23)In project: com.example.pizza; open package: com.example.pizza.actions; class: SampleActions.java; method: run( ) change:
if (element.getAttribute("cost") != null) { cost = element.getAttribute("cost"); } with: if (element.getAttribute("class") == null) { if (element.getAttribute("cost") != null) { cost = element.getAttribute("cost"); } } else { try { ITopping topping = (ITopping) Class.forName( element.getAttribute("class")).newInstance(); cost = String.valueOf(topping.getCost()); } catch (Exception e) { cost = e.toString(); } }24)Use Source; Organize Imports
25)RC. Project com.example.pizza; Run as; Eclipse Aplication;
26)Close Welcome screen; Press button: : Hello, Eclipse world
27)Dialog box: Installed pizza toppings; Message: ham(java.lang.ClassNotFoundException: com.example.pizza.ham.HamTopping); OK;
28)Close 'runtime' workbench/Eclipse (oposite to 'host' workbench/Eclipse); OK
29)In project: com.example.pizza; open package: com.example.pizza.actions; class: SampleActions.java; method: run( ) change:
ITopping topping = (ITopping) Class.forName( element.getAttribute("class")).newInstance(); with: ITopping topping = (ITopping) element.createExecutableExtension("class");30)RC. Project com.example.pizza; Run as; Eclipse Aplication;
30)Select resources to save; OK
31)Press button: : Hello, Eclipse world
32)Dialog box: Installed pizza toppings; Message:
cheese(1)
ham(2)
pepperoni(1)
OK;
33)Close 'runtime' workbench/Eclipse; OK
34)RC. Project com.example.pizza; Export; Deployable plug-ins and fragments; Next
35)Available plug-ins and fragments: Select All; Destination; Directory: Browse?; Options; Include source code: check; Finish
Boris Starchev teacher Ruse Bulgaria
bstarchev@ru.acad.bg
Re: Getting started with Eclipse plug-ins: creating extension points
Thanks for your valuable guidance..But after completing step 24 ..and if run the com.example.pizza as an eclipse application.It is opening another Eclipse window (i mean another eclipse editor)..iam not getting the output window... Pls tell me where i would have gone wrong.. Waiting for your reply with hopes..L.Thiyagarajan
Extension Overriding problem.pls help me out
Thanks for your valuable guidance..But after completing step 24 ..and if run the com.example.pizza as an eclipse application.It is opening another Eclipse window (i mean another eclipse editor)..iam not getting the output window... Pls tell me where i would have gone wrong.. Waiting for your reply with hopes..L.Thiyagarajan
Re: Getting started with Eclipse plug-ins: creating extension points
Hi,I have a problem - getConfigurationElements() is always returning an empty array.
I even tried, IExtension[] extensions = extensionPoint.getExtensions(); but still the array is empty.
I downloaded a sample project and debugged that to see could be wrong in my plug-in. I found that, the extended plug-ins get listed under objectManager --> namespacesIndex hashtable(downloaded project).
In my project the extended plug-ins get listed under objectManager --> extensionPoints hashtable.
Any guess, what could be the problem?
If you need any particular info I shall provide...
Thanks,
Jams
Re: Getting started with Eclipse plug-ins: creating extension points
I was able to create the com.example.pizza plugin and see the dialog showing the "cheese (1)". I created the second plugin, com.example.pizza.ham, but I'm not able to see the additional toppings.I ran with the -console argument and here is a subset of the plugins. As you can see, the ham plugin is resolved. I've also ran with the -consoleLog to verify there were no errors. Any idea what the problem may be?
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.1.R33x_v20070828
1 ACTIVE org.eclipse.equinox.common_3.3.0.v20070426
2 ACTIVE org.eclipse.update.configurator_3.2.101.R33x_v20070810
3 ACTIVE org.eclipse.core.runtime_3.3.100.v20070530
4 RESOLVED com.example.pizza.ham_1.0.0
5 > com.example.pizza_1.0.0
6 RESOLVED com.ibm.icu.source_3.6.1.v20070906
Re: Getting started with Eclipse plug-ins: creating extension points
Try deleting the launch configuration and re-creating a new one. Sometimes the PDE cache stores out-of-date information with respect to the extension points.Alex
Re: Getting started with Eclipse plug-ins: creating extension points
Try deleting the launch configuration and re-creating a new one. Sometimes the PDE cache stores out-of-date information with respect to the extension points.Alex