Showing posts with label extensions. Show all posts
Showing posts with label extensions. Show all posts

Eclipse plugins and Extensions

Posted by U.S. Wickramasighe | Posted in , | Posted on Thursday, March 18, 2010

Eclipse plugins architecture is an extended layer of OSGi. It basically wraps OSGi layer (such as Equinox) to provide a rich framework for it’s plugins and extensions. What that obviously means is , deepdown inside runs the same mechanics of imported/exported bundle packages and their dependency loading runtime as described/portrayed in OSGi specs/platforms. Eclipse plugin information is defined in a configuration file called plugin.xml .Eclipse’s plugin loader (or kernel) is an implementation of OSGi R4 specification that is responsible for loading and unloading plugins. However Plugin Loader does not load all the dependencies(ie:-classes/libraries) at the start-up since doing so would be an overhead for most of the systems (especially very large/complex systems). Plugin Loader employees a mechanism called “lazy loading” to load plugins and dependent classes and packages only when they are needed at the later most possible time. It actually do this by constructing a dependency hierarchy at startup by reading the information contained in plugin.xml of all the available plugins and loading them in to memory. So when a particular plugin wants to load classes from a different plugin (exported by the plugin/bundle) it would load them using the EclipsePlugin Registry (memory structure that was loaded at the startup) , as and when needed by the programming flow.
Eclipse’s way of dependency management is two fold , which are

1)Creating dependencies
-describes which external plugins are required by the plugin at hand. For example for the operation of a plugin A we may require external classes/packages exported by a specific plugin B ,which can be depicted as “Plugin B required by PluginA”. By doing so “plugin A” get exposed to all the packages/classes exposed by “plugin B” . This is similar to specifying [Require-Bundle: PluginB] in Plugin A’s MANIFEST.MF . Some times a plugin may only need several exported packages of a specific plugin ,not all of them ,which can be depicted as “package a,b,c of Plugin B required by PluginA” . This is similar to specifying [Import-Package: b.org.ui] in Plugin A’s MANIFEST.MF . However there’s an additional complication .For example If another plugin C exports a package with the same name "b.org.ui" ,then there’s a chance of conflict to arise since we are no longer depending on a specific plugin but a package.

2)Creating Extensions and Extension Points
-defines how the plugin at hand can be involved in extending the functionality provided by other plugins (extensions) and what points of contact can a plugin provide so that other plugins can augment their functionality using the core functionality given –( extension points) . In other words extension-point acts as a pluggable interface/abstraction for outside plugins, while extensions act as a plugged component that has enhanced functionality. We can define extensions and extension-points in plugin.xml file .A respective Extension-point is normally written according to a specific schema (defining extending class ,name,plugin specific parameters,etc ) and plugin extensions should be solely responsible to extend accordingly to their respective schemas of the extending extension points. For Example we can create a customized View Panel in Eclipse by extending "org.eclipse.ui.views" extension point of org.eclipse.ui plugin . In our plugin say “org.simple.demo” we can have the following extension defined in our plugin.xml to create our customized view.


For above plugin to work properly we have to create "org.simple.demo.views.DemoView" class and extend it accordingly as specified in the documentations to "org.eclipse.ui" package classes. (ie:- more specifically by extending org.eclipse.ui.ViewPart and overriding #createPartControl(Composite) method ) .if the extension plugin is successfully deployed , as defined in the extension ,our view panel should appear under “DemoCategory” with the name “Demo view Panel” and it would have the ”sample.gif” icon on the title bar of the panel . (by intuitive observation we can see that this extension schema allows us to define multiple views under same category by declaring the same category id on view tag’s attributes for each view. However as good practice you should always go through the schema documentation of the relevant extension points for further clarification) .
It would be very interesting to look into how our extended plugin actually works at runtime . As mentioned at the beginning plugin-loader would load necessary information about different plugins at startup (ie:-known as creating Eclipse ExtensionRegistry) . Our plugin is actually an extension of "org.eclipse.ui" plugin ,hence using the information gathered at the startup(ie:-extension information of all the plugins that has extended on org.eclipse.ui.views extension- point) "org.eclipse.ui" would load all it’s extended views into the main panel displaying each and every category and their symbolic representation of views under them. Actual View will only be loaded(using the ExtensionRegistry information ie:-class name respective to the extension) when a user actually clicks the relevant view entry on the main panel containing list of views. This is known as “lazy loading” ,where actual class, in this case org.simple.demo.views.DemoView is loaded on demand.

Note that if we plan to create a plugin manually we should precisely reflect our intentions(especially related to dependency management) in plugin.xml (which is the file that eclipse abstraction layer operates on) as well as MANFEST.MF( which is the file that underlying OSGi runtime operates on). This could be especially difficult if we consider the unintuitive specifications of bundle properties needed to be written in Manisfest.MF (as in exporting, requiring bundles, naming conventions,etc) as well as numerous xml tagging schemas needed in specifying extensions and extension points which will drastically vary on plugin we are going to extend. Fortunately Eclipse provides plugin developers with PDE (Plugin development Environment) which gives a nice an easy user interface to workwith together with a elegant set of tools to seamlessly integrate and extend plugins without much hassle. Eclipse Plugin Manifest Editor categorize itself into five main sections/pages in creating a new plugin .Changes made in this sections directly reflects in MANIFEST.MF most of the time.

• Overview - defines plugin name, id (uniquely identifies a plugin ) , version, description, etc. You can also optionally declare a plugin Activator class if you want system setup code to be put in

• Dependencies - responsible for creating plugin dependencies (ie:-Required plugins/bundles and imported packages)

• Runtime - Defining exported packages for the consumption of other plugins(ie:- export package) .Additionally you can define the bundle classpaths for external jars for use within the plugin.

• Extensions - defining extensions and related parameters.

• Extension Points - defining extension points for a plugin and it’s schema


[Snapshot of Plugin Manifest Editor]

Code behind Extension Points

You may wonder how a plugin can load the extensions (may be declared in itself or by other plugins – most of the time ) defined by the respective extension-point it has. As mentioned at the beginning answer lies in the Eclipse extension Registry which eclipse kernel builds at startup. For this to materialize extension Registry needs the Extension-point Name on which the extensions are declared and the respective Plugin-ID .Following code snippet shows the code needed to acquire the declared extensions.


IExtension[] extensions = Platform.getExtensionRegistry().getExtensionPoint(“org.eclipse.ui”, "views").getExtensions();

for (int i = 0; i < extensions.length; i++) {

IConfigurationElement[] configElements =
extensions[i].getConfigurationElements();
}

“extensions” return an array extensions extended by extension-point “org.eclipse.ui.views” .”configElements” is an array of elements defined under each extension as declared in plugin.xml . For example in our sample ui panel “DemoView” scenario , this relates to the xml elements and declared in our “org.simple.demo” plugin extension under element. Each IConfigurationElement is a XML object model element which can be traversed in a tree like manner . An IConfigurationElement can be used to load classes as well ,if their attributes do contain a class element (ie:-class Name) as in the case where element contained a attribute named “class”. Following code snippet demonstrates this.

try {
view = (ViewPart) viewConfigElement.createExecutableExtension(“class”);

} catch (Exception e) {

Log.logError(
"Failed to instantiate factory: "
+ viewConfigElement.getAttribute(“class”)
+ " in type: "
+ id
+ " in plugin: "
+ configElement.getDeclaringExtension()
.getNamespaceIdentifier(),e);
}

This is the most common way of loading classes dynamically on eclipse plugins ,especially as in “lazy loading” scenarios.

This article only gave a basic understanding on the mechanics of eclipse platform –dependency management, extensions and extension points ,etc. There is lot more to be explored in eclipse and lots of API’s to be figured out to build a useful Eclipse RPC or Plugin App. However I hope my article series (including OSGi) have given you that initial stepping stone for achieving that goal. Please feel free to put feedback ,your ideas,etc here as well.


Insight into OSGi and Eclipse Extensions

Posted by U.S. Wickramasighe | Posted in , , , , , , , , | Posted on Thursday, May 14, 2009

I've been working on OSGi(Open Statndards Gateway initiative) and Eclipse extensions for a while. Both are of course very good frameworks (infact Eclipse Extension framework depends on OSGi ) that has sprung up not very long ago and has become very popular gradually. Obviously Eclipse's popularity as a project and IDE has served immensely towards the recognition of OSGi as a framework. Technically speaking Eclipse and all it's components (except it's kernel) runs on top of an OSGi framework implementation named equinox. Hence lot of software products and platforms are now turning into OSGi to make their software more extensible or scalable.

As most of you know , within the core of OSGi lies a class-loading mechanism , where each bundle (as so called in OSGi) would take the responsibility of loading classes of their own , and would facilitate exposing of a subset of classes to the outside world while others will remain internal to the respective bundles. Although OSGi uses same Jar and Manifest mechanisms to achieve this, it is a radical change from Sun's Jar specification where all the classes and packages are implicitly exposed while clients can use any class within, they want (of course if the respective class implementation supports it). It's OSGi Runtime that deals with loading the bundles , managing dependencies(resolving) and registering and consuming services...

OSGi Service is simply an plain old java object (POJO) which can be either state-full or stateless , or any kind of a java Object that does some useful stuff. These objects should be registered under a interface key name and hence in order to register correctly as an OSGi service , service objects need to implement the specified Interfaces. Inorder to register a service you need the BundleContext object , which is almost always taken from a Bundle Activator provided by the OSGi framework to the "start(BudleContext)" at the start of each bundle.Following shows an example..

public class TestMailboxActivator implements BundleActivator{
public void start ( BundleContext context ) throws Exception {

IMailbox mbox = new TestMailbox ( ) ;
Properties mailboxProps = new Properties();
mailboxPropos.put("name","FixedTestMail");
//registering the service under it's interface name
context.registerService(IMailbox.class.getName(),mbox,mailboxProps);
}
public void stop ( BundleContext context ) throws Exception { }
}
}
For the classes to work and resolve properly ,IMailbox Interface should be exposed in the respective bundle(as declared in "export package" Manifest entry) or should be in the same bundle as TestMailbox (which is usually not the case). As in the example , services can be provided with key,value pairs as metadata which will be very useful indeed. However consuming services is little more challenging than registering them especially since we don't know when will services come and go due to their inheretly dynamic behavior.

We may not know when will a bundle register a service object and when it will be destroyed.One way to resolve this problem is to always keep querrying service eachtime it need to be accessed as shown below.

ServiceReference ref = context.getServiceReference(IMailbox.class.getName());
if(ref != null) {
IMailbox mbox = (IMailbox)
context.getService(ref);
if(mbox != null) {
int count;
try{
count = mbox.getMessageCount();
}
finally{
context.ungetService(ref);
}

return count;
}
}

The method "ungetService" will tell the framework that it's nolonger using the service.This important since framework keeps a count of a particular service and when it reaches zero it will get it disposed.However using a ServiceTracker would make the above procedures a lot easier as well.

ie:-

ServiceTracker mboxTracker = new ServiceTracker(context,IMailbox.class.getName(),null);
mboxTracker.open();
IMailbox mbox = ( IMailbox ) mboxTracker . getService ( ) ;

Also Service trackers come useful when managing particular services that depend on other services as well. Suppose a service object "servicePayroll" depends on another service "payment" , where payroll object is useless without having payment in it's hand. Here "servicePayroll" service is dependant on "payment" and hence it is pointless to consume "servicePayroll" object if system already does not have a "payment" service registered in it. To solve this problem , we have listen in for the "payment" sevice to appear (when someone registers a "payment" service) and then register "servicePayroll" object so that now payroll is able to process payments and provide necessary details for it's clients.

However things get little complicated when implementing this solution. One reason is we have to take notice of the "payment" services registered before our listener has even started as listener does not take care of this matter. So we should look up service registry to take into account the previously registered "payment" services. Also while we looking up the registry there may come a moment ,a "payment" object has been registered , however since we haven't registered our listener yet , this will go unnoticed. So we should register our service listener to listen to "payment" services first and then start lookup secondly.Again there will be some complications in overlapping and duplicate services so we should take care of them as well..

This obviously is very complex and cumbersome code and that's where Sevice Trackers come into the rescue.ServiceTracker objects handle these scenarios for us and provide us with nice and easy interface to work with..Service tracker code is almost the same , but additionaly to handle listening and registering of dependent services we have to provide service tracker object with new "ServiceTrackerCustomizer" object..it Provides very useful and important addingService(ServiceReference) method , which is invoked whenever tracked service is found in the registry or when someone registers it.


ServiceTracker paymentTracker = new ServiceTracker(context,IPayment.class.getName(),new PaymentTrackerCustomizer(context));
mboxTracker.open();

public Class PaymentTrackerCustomizer implements ServiceTrackerCustomizer(){
private BundleContext context;
.......

public Object addingService(ServiceReference ref){
payment = (Payment)context.getService(ref)
Payroll proll = new Payroll(payment);

//Returns ServiceRegistration object
return context.registerService(IPayroll.class.getName(),proll,null);
}

.........
}

addingService can return either null or any object (in this case ServiceRegistration object). Then when ever you call ServiceTracker's (ie:-paymentTracker) getService() method you will get the returned object (from addingService()) as the service object.

So this is so far for now , about OSGi and Services. I'll talk about Eclipse extensions and extension points with the next post..So keep n touch and Thank you for reading...