After having implemented the server application, we want to prepare ourselves for future changes. We want to be able to add new services to our application without impacting existing ones. We also may want to change the structure of the URIs in case they are not cool.
To do so, we’ll create extension points and the supporting code for the Restlet types used and contribute our implementations and see how that this helps us making our application more flexible and maintainable.
We’ll learn how to define an extension point, how to contribute to it and how to work with the contributions.
PrerequisitesYou should have skimmed over the first two posts of this series. If they do not contain new information for you you to not have to work through them, however since we are going to build upon the results from the last post, you should get the latest code from the post2 branch1Â if you do not have it yet.
Introducing Extension PointsThe extension point mechanism is a powerful feature that allows multiple bundles to communicate with each other. A bundle defining one or many extension points basically says “I am extensible. You can add your extension code here. “. An extension contributed to an extension point provides additional functionality to its bundle. The twist is that you can extend the bundle providing the extension point without making any changes to it. This isÂ particularlyÂ useful when you want to customize/build upon an application withoutÂ modifyingÂ it2.
The extension point is a piece of XML schema that defines how contributions to that point are to be structured, i.e. which attributes they need in which cardinality and of which type. The contribution to the extension point is made in the plugin.xml file of the contributing bundle. In addition to that, there needs to be some code that evaluates all contributions to the extension point and provides them to the core implementation. Note that this also separates the implementation of single components (code) from their use (contribution). In my experience, this leads to a significantly improved overall design as the definition modular structure of an application is clearly separated from its implementation and people tend to think about modularization earlier and more clearly as a part of the development workflow3.
Eclipse and othersÂ uses this paradigm e.g. to provide IDEs in very different flavors4 5 6, but they all share a common core. Â I daresay that this Â is one of the key factors to the success of Eclipse and its wonderfully vibrant and diverse ecosystem. The implementation ofÂ extension mechanism is provided by the Eclipse Equinox project 7. You could argue that a similar result can be achieved by using plain OSGi (declarative) services, perhaps we’ll do so in a later post. For a comparison refer to Neil Bartlett’s excellent posts8 9.If you want to dig deeper, there is quite a number of useful articles on that topic out there 10 11 12 13.
The Restlet Registry – Extension PointsThe backbone of our new architecture will be the Restlet registry. It will contain the definitions of our extension points Â and the supporting code that evaluates contributions to these extension points. Our final registry will consist of multiple extension points:
Let’s start with the first extension point, Resources. A resource consist of an ID and a corresponding implementation class. First, activate the editors for extensions in the new project’s properties.
To be able to reference the the ServerResource interface, it needs to be added to the registry bundle’s dependencies.
After that, open the MANIFEST.MF file from the META-INF folder an go to the extension point tab. Click “Add” to add a new extension point. Enter ‘resources’ as ID 18 and ‘Restlet Resources’ as name.
Clicking “Ok” takes us the extension point schema editor. You should really enter useful information on how the extensions contributed will be handled. The function of your extension point may seem clear to you now, but remember you are building an interface for public (more or less) consumption.
Now let’s go to the “Definition” tab and add a new element “resource“. Add the attributes “id“, “name” and “class” to the new element. IDs Â and names should have the type String; Classes the type Java with the restriction that contributed classes must implement Restlet’s ServerResource interface. Then select the ‘extension’ element and add a “New Sequence” of the cardinality 1:n. Add a reference to the “resource” element to the new sequence.
The next extension point to implement Â is the one for Restlet Components. It should be able to handle a collection of component elements, each with a name for human (developer) readable representation, a list of servers it uses (each with protocol and port) and a list of referenced resources. Note that if you create references to IDs, you can also restrict the element you want to reference. Go ahead and try to create the extension point on your own now. If you get stuck, the screenshots19 below should help.
- resource (1:*)
- id (String)
- name (String)
- class (Java, implements org.restlet.resource.ServerResource)
- resource (1:*)
- component (1:*)
- name (String)
- a Â list (1:*)Â of elements of the types
- name (String)
- protocol (String)
- port (String)
- resourceReference Â (1:*)
- urlTemplate (String)
- resourceId (Identifier, Restlet Resources/@id)
- component (1:*)
Go back to the original net.wolfgangwerner.tutorial1.restlet bundle and open the plugin.xml file and go to the “Extensions” tab. Click “Add” to add a new extension point to contribute to. In the following dialog, deselect “Show only extension points from the required plug-ins”. Select the resources extension point we just created and click “Finish”. When prompted, confirm you want to add the registry plug-in to the list of required bundles.
Since we specified a lower bound of one element for the resource element, one has already been created for us. PDE is smart enough to parse our extension point schema and configure the editor correspondingly. 20
Enter an ID, a name and the implementing class. The ID is used later to attach the resource to the Restlet Component. You should use a fully qualified ID (i.e. including the bundle name) here to make it unique within the runtime21. Though you can choose any ID you like, using a fully qualified one protects you from name clashes with extensions in other bundles. The name will be used as label in the extension editor; use a label that helps you identifying it once you contributed a larger number of resources. To chose the implementing class, you can use the code assist shortcut Ctrl+Space.
Repeat the last steps to contribute the Greeting List Resource in the same manner.
Then add a component extension and use its context menu to define two servers (e.g. HTTP on port 80 and on port 8080). Then add a resource reference element, enter “/greet” as URL template and browse for the corresponding identifier22.
The Restlet Registry – ImplementationNow that we have defined our extension points and contributed our code to them, we need to write the code that actually does something with this information.
As we need to reference the registry bundle to get its extension points, we add an ID to its Activator. The ID is the same as the bundle id of the registry bundle23.
To keep the code that evaluates the contributed extensions separate from our bundle life cycle code, we create a new class called RestletRegistry that encapsulates . We can simply ask the Platform to give us all contributions to a specific extension point. Â We specify the extension point by its namespace 24 and the name of the extension point. We iterate over the list of contributions and save the contents in our Restlet Registry. This is straight forward for the ‘id’ attribute but requires some thought for the resource class. While there is a shortcut to create an executable extension (read: Object) from a class attribute, this doesn’t help us: The Restlet API requires us to attach the resource’s Class. In a non-OSGi environment, we could simply call Class.forName(String), but this will fail here as each bundle has a separate class loader. Instead, we need to get the contributing bundle and let it load the class for us using its own class loaded25.
Getting the data from the component extensions is pretty straightforward. We get the extensions as we did for the resources, create a Restlet component for each extension, then add the corresponding servers to it and attach the resources at the given URL templates.
To keep our code clean26, we split the parsing into several small methods27.
You’ll have noted that there are several exceptions that can be thrown when working with extensions. In real life, you’ll need to safeguard your code against invalid extensions28. Invalid extensions should be ignored and logged but may not impact other extensions contributed to the same extension point or obstruct the operation of the core.
In the last post, we started our server from the Activator of the first bundle. Since we are going to start all contributed components instead of hardcoding them, we can delete this Activator class and remove it from the Manifest.
Now all we have to do to make it run is to tie the registered component’s life cycle to the one of our registry bundle and adjust our launch configuration to include and auto-start the registry bundle.
They tried to make me go to recap …You just created a small framework to build Restlet based web services and run them on the equinox OSGi runtime. You started by building an OSGi bundle that contained a simple Restlet web service. You rolled this bundle into a standalone server application. In the second post, you extended the application and learned a little bit about Restlet. Along the way, you also picked up some OSGi knowledge.
In this post, we improved our application by allowing the contribution of additional resources from other bundles as well.
The application is not yet finished, I left some work for you29. The next step is to create more extension points to allow the contribution of other Restlet subclasses (Filters, Applications and Routers) and finish the migration of the version from post #2.
Prior art includes the presentation of Shams and Norris on eclipsecon 200830. Originally I wanted to give the code they created a try, but was deterred by a strange license and download process. Others have noticed that as well 31.
I will wrap the final results of the Restlet Registry with all extension points into a project ready to use, stay tuned 32 if you are interested. I’d also be happy to discuss whether the current extension point setup matches real world requirements and how it could be improved. Implementing a Restlet OSGi service is also very conceivable. Let me know if you want to help or simply go ahead and fork the repository.
- git clone git://github.com/wwerner/EquinoxRestletTutorial.git
cd EquinoxRestletTutorialor http://github.com/wwerner/EquinoxRestletTutorial/zipball/post3 ↩
git branch -a
git checkout -b post2 origin/post2
- Standard Software + Customizing / ISV + Implementation Partners, anyone? ↩
- “Oi, this class shouldn’t call that class from the other layer”
“Which other layer?”
“Well, you can see which layer a class belongs to by it’s name. We have a naming convention for that.”
“Oh, Bob never uses it. He finds that the names become unreadable when applying it.”
“It also is in the wrong package.”
“I did’t import it! Strg-O did, so I figured I could use it.”
- See http://www.eclipse.org/downloads/ ↩
- Â http://www.borland.com/de/products/together/index.html ↩
- Â http://www.sdn.sap.com/irj/sdn/nw-devstudio ↩
- http://www.eclipse.org/projects/project_summary.php?projectid=rt.equinox ↩
- http://www.eclipsezone.com/articles/extensions-vs-services/ ↩
- Or use his e2s project as glue:Â http://github.com/njbartlett/Extensions2Services ↩
- Â http://www.eclipsezone.com/eclipse/forums/t93753.html ↩
- http://www.eclipsezone.com/eclipse/forums/t97608.rhtml ↩
- http://www.vogella.de/articles/EclipseExtensionPoint/article.html ↩
- http://www.sigs.de/publications/js/2008/01/hennig_seeberger_JS_01_08.pdf (german) ↩
- At some point in time ↩
- Partly because I’m not sure how they should look like exactly ↩
- And also to demonstrate the interaction of multiple bundles ↩
- http://blog.wolfgang-werner.net/building-on-equinox-and-restlet-1/#Creating_the_Application_Bundle ↩
- The fully qualified ID will be the plug-in ID plus the extension point ID, so you have to worry about uniqueness only in the namespace of your bundle. ↩
- Or – of course – the code ↩
- Never really noticed itÂ consciously. ButÂ actually pretty neat! ↩
- The extension point schemas got that prefix from the wizard ↩
- Neat ↩
- This is a little WET (Write Everything Twice ||Â Wicked, Evil and Tedious). Not DRY. You could also obtain the current bundle from the BundleContext in the Activator and use its symbolic name ↩
- i.e. the ID of the bundle as added to the activator ↩
- Note that making the registry bundleÂ dependent on the contributing bundle is not an option. This would force us to change the generic registry whenever a new bundle contributes a resource. The whole point of having an extension point is to have independent/third party code extend core functionality w/o modifying the coreÂ ↩
- http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882 ↩
- Alt-Shift-M or Alt-Shift-T, x is your friend. ↩
- What if a contributed resource is not derived from ServerResource? What if the extension does not provide all required attributes? ↩
- Also, the post is getting long. And it is getting late. ↩
- http://www.eclipsecon.org/2008/?page=sub/&id=462 ↩
- http://bryanhunt.wordpress.com/2009/11/23/updating-ensemble-for-restlet-2-0-license-beware/ ↩
- Â http://twitter.com/0xcafebabe,Â http://github.com/wwerner ↩