This website uses cookies to ensure you get the best experience. Learn More.
Extensible templates: the OSGi way
All the OSGi related contents we have seen so far during previous blog entries are related to backend systems. There is no other reason that my daily work basics happens under the services, sorry about that. I will try to correct this situation with an example on how we can build extensible user interfaces using the already builtin mechanisms into the platform.
Disclaimer: my most sincere apologies with all the damage my poor design skills could cause
We want to write a new Liferay application with an extensible UI, so third-party components can write their custom extensions, contributing to our UI with new elements (even replacing them)
Our solution will be based in the BundleTrackerCustomizer concept and the Extender Pattern. We could use other approaches, based in services, but the extender pattern fits perfectly with our current template mechanism.
Using a BundleTrackerCustomizer we can get notified every time a new bundle is deployed into the OSGi container. This bundle will track all the extensions, indexed by name. The following class implements the tracking logic
As you can see in the previous class definition we are just interested in the plugins which present certain kind of attributes: the Templates-Location one. This attribute will contain the extension point with the location of the templates. Once the tracker detects this deployment, it will store the reference to all of this locations
Note this is just a proof of concept and need quite a few improvements. Hopefully we are going to use this idea to create some new applications and we will generalize this component with some nice features:
Now we need to create our extensible views, defining those points where we want third-party components can contribute with their custom views.
Lets create a simple view file where we create a very simple extension:
<div id="template-extensions">
<#assign extensions = request.getAttribute("sampleProviderFactory").getRegisteredTemplates() />
This is just a very simple test showing all the registered extensions contributed by plugins: <br />
<#list extensions as extension>
<#include "${extension}" />
</#list>
</div>
As we can see in the preview snippet, we are just getting all the references to the existing contributions and including them. As you can imagine, we could do this extension mechanism much more powerful by using extension names and letting the contributions to decide which element they want to contribute. As I noted before, we will try to generalize this idea and create a customizable component.
We have a few limitations at the time of this writing I hope we can simplify at the future:
They are not real problems but something we need to deal with in order to put our solution in place
Writing our first extension is a pretty simple task: we just need to create a new bundle, and define the mandatory attribute in order to be detected by the template tracker. You could use the following bnd file to create your extension
Bundle-Name: Sample Metrics Portlet Extesion
Import-Package: \
com.liferay.portal.kernel.bean,\
com.liferay.portal.kernel.log,\
com.liferay.portal.kernel.servlet
Templates-Location:/templates
Web-ContextPath: /sample-metrics-portlet-extension
-wab: \
docroot,\
{WEB-INF/web.xml=docroot/WEB-INF/web.xml}
-wablib: \
${app.server.portal.dir}/WEB-INF/lib/util-java.jar
I have not gone through all the details since I just wanted to highlight the main concepts. Feel free to ping me if you want some low level details.
See you soon!