Forum Controls
Spotlight Features

The Rich Engineering Heritage Behind Dependency Injection

Andrew McVeigh takes us on a tour of the rich heritage behind dependency injection, what it represents, and tells us why its here to stay.

Java, the OLPC, and community responsibility

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!
Replies: 11 - Pages: 1  
Threads: [ Previous | Next ]
  Click to reply to this thread Reply

Getting started with Eclipse plug-ins: creating extension points

At 7:08 PM on Jun 19, 2007, Alex Blewitt DeveloperZone Top 100 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 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:

<plugin>
  <extension-point id="com.example.pizza" name="Pizza toppings" schema="schema/com.example.pizza.exsd"/>
</plugin>

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:

<extension point="com.example.pizza">
  <pizza cost="1" topping="cheese"/>
</extension>

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:

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());
}

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:

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 , 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:

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:

<attribute name="class" type="string">
 <annotation>
  <appInfo>
   <meta.attribute kind="java" basedOn="com.example.pizza.ITopping"/>
  </appInfo>
 </annotation>
</attribute>

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.

  Click to reply to this thread Reply
1. At 5:25 AM on Jun 22, 2007, Philippe Gregoire Javalobby Newcomers wrote:

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
  Click to reply to this thread Reply
2. At 7:40 AM on Jun 22, 2007, Alex Blewitt DeveloperZone Top 100 wrote:

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.

Alex.
  Click to reply to this thread Reply
3. At 7:25 AM on Jul 5, 2007, Vipul Lalan Javalobby Newcomers wrote:

Re: Getting started with Eclipse plug-ins: creating extension points

Very nice article
Proved very useful!!!!!

Thanks
Vipul
  Click to reply to this thread Reply
4. At 7:32 PM on Jul 25, 2007, Alex Blewitt DeveloperZone Top 100 wrote:

Re: Getting started with Eclipse plug-ins: creating extension points

I've posted the next installment in this series.
  Click to reply to this thread Reply
5. At 12:15 PM on Jul 28, 2007, starchev Javalobby Newcomers wrote:

Re: Getting started with Eclipse plug-ins: creating extension points

Creating Extension Points 1 - details instructions Eclipse 3.3

1)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:
MessageDialog.openInformation(
			window.getShell(),
			"Hello_World Plug-in",
			"Hello, Eclipse world");

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
  Click to reply to this thread Reply
6. At 11:17 PM on Oct 28, 2007, Thiyagarajan Javalobby Newcomers wrote:

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
  Click to reply to this thread Reply
7. At 8:01 AM on Oct 31, 2007, Thiyagarajan Javalobby Newcomers wrote:

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
  Click to reply to this thread Reply
8. At 10:18 AM on Dec 17, 2007, jams Javalobby Newcomers wrote:

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
  Click to reply to this thread Reply
9. At 10:34 AM on Jan 8, 2008, Brian Occasional Javalobby Visitor wrote:

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
  Click to reply to this thread Reply
10. At 10:26 AM on Jan 9, 2008, Alex Blewitt DeveloperZone Top 100 wrote:

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
  Click to reply to this thread Reply
11. At 10:26 AM on Jan 9, 2008, Alex Blewitt DeveloperZone Top 100 wrote:

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

thread.rss_message