Monday, February 8, 2016

Liferay OSGi Container - Quick Overview

What is OSGi?


In general, it is a specification that defines an architecture for modular application development. So what does it mean? The application is designed in such a way that it is having multiple extension points and the variety of the extensions can be developed during the course of the time. Those extensions developed are independent of the application and there is no dependency on each other. They live independently. Moreover, the extensions can be removed at any point of time desired.


The acronym of OSGi is Open Services Gateway Initiative. It is also known as Dynamic Module system for Java, defines two things:


  • a set of services that an OSGi container must implement
  • a contract between the container and your application.


Developing on the OSGi platform means first building your application using OSGi APIs, then deploying it in an OSGi container. From a developer's perspective, OSGi offers the following advantages:


  • You can install, uninstall, start, and stop different modules of your application dynamically without restarting the container.
  • Your application can have more than one version of a particular module running at the same time.
  • OSGi provides very good infrastructure for developing service-oriented applications, as well as embedded, mobile, and rich internet apps.

OSGi in Liferay


Starting Liferay 6.2 it ships with the OSGi container.  What does that mean? Portlet applications can be designed and developed as modular applications with extension points and can be deployed on to Liferay provided OSGi container as a modules. Thus inheriting all the modular characteristics to a portlet application. Content Targeting Applications that is available in the marketplace, is built on the OSGi in 6.2. Liferay 6.2 and 7.0 (which is yet to be released) are developed with lot of extension points, providing users the flexibility to extend as per their complex use cases and needs.


In clustered environment there is no distributed OSGi container. Need to deploy on all the nodes. This may be a feature consideration for future releases. As such OSGi specification does not address this distributed containers or remote containers.


Enabling OSGi Container in Liferay 6.2
Though the OSGi container in the Liferay 6.2 is not well matured, liferay engineers provided alternative ways to make it work. As OSGi container is not enabled by default, one need to follow below steps to make it work.


  • Get the OSGi required bundles from liferay.
  • OSGi bundles are present in the data folder under <<liferay_home>>/data/osgi.
  • Create a folder called “modules” in the  folder osgi and place all the required bundles.
  • Add the following properties in the portal-ext.properties
    • module.framework.enabled=true
    • module.framework.properties.osgi.console=11311
The second property above specifies the OSGi console port number it would be running.
  • Restart the Liferay server.
Liferay is now configured with the OSGi Container and ready to be deployed modules on to it.


OSGi Console


Below are list of some important operation that can be performed in the OSGi Console


  • To access the OSGi console, in a terminal run the command
    • telnet localhost 11311
  • To see the bundles already in the container, run command “lb”. If no modules are deployed, by default it should list 16 modules.
  • The bundles will be listed  with their states (Active/Resolved/Installed)
    • Active means it is ready to be used.
    • Installed - Bundle is present but not started. We need to start it to make the status Active.
  • Starting the bundle run the command
    • “start <<component ID>>”
  • The bundle will only be started if all its dependencies are available and resolved. If not it errors.
  • To check the error, run the command
    • “diag <<component ID>>”
  • To list the Services registered in the Service Component Runtime SCR, run the command
    • “scr:list”
  • To stop the bundle run
    • “stop <<component ID>>”

The life cycle of the bundle is depicted in the following picture



Portlet Plugins as Modules


Portlet plugins can be developed as the osgi bundles and can be deployed on to the Liferay OSGi container. The portlet plugin has to conform to the OSGi specification in order to deploy it on the OSGi container. So liferay developed a process to convert the Portlet Plugins as osgi bundles. This conversion process converts the .war file .wab. .wab is called web archive bundle which can be deployed on to the OSGi Container. Liferay provides the pom file for 6.2 version for this conversion process. These wab files will be deployed to folder data/osgi/bundles not in to the default deploy folder where the war files are deployed.


Below picture shows the OSGi container that is present in the liferay server and it default services and bundles.


osgi-container-liferay-62.png


All bundles deployed on the OSGi Container will live only in the liferay osgi container.  


Some limitation of Liferay 6.2 OSGi Container


  • Can not automatically convert existing war files to osgi bundle
  • HTTPService is required. It is proxy from equinox
  • It does not support JSP’s. It supports templating languages like freemarker


All these limitations are addressed in Liferay 7.0

Developing a portlet plugin as a liferay OSGi Bundle


Let us consider a small fictitious chat application. This application contains a text box where user can send a message to other users in the liferay annotating their screen name. For now let us assume we are just displaying the list of messages that are entered in the text box, on the same screen (instead of sending it to the real user).


So the annotation of the screen name can be captured and we can write different kinds of filters, one of which would be replacing the annotation and screen name with the First and Last name of the user. We can write variety of filter on the annotated screen name.  


We can develop this whole application as a complex application with all the filters self contained in this application only. Would’nt it be nice to separate the separate the annotation interpretation logic and develop  different filters as independent plugins, so that they have their own lifecycle? OSGi makes it possible to do so. Here annotating the screen name is the extension point and the  defining different replacements for that annotated screen name are the extensions.

For this application we need three plugins
  • Portlet bundle - For the UI - as wab file
  • API - Containing the service registry and the contract (interface) - as jar file
  • Service - Implementation of the api - as jar file


TextFilter Extension


This is the interface which contains methods that any text filter extension must implement. The implementations of this interface are annotated with @Component so that OSGi will detect dynamically when a new extension is deployed on to the OSGi container.


@Component(immediate = true)
public class UserNameFilter implements TextFilter {


Referencing other services that are in the OSGi container is achieved with the annotation @Reference. In our example UserNameFilter,  we would be accessing the UserLocalService which is a liferay service. All the liferay services are bundled into OSGi container and made available to all other services that are deployed into OSGi container. Here is the code depicting the referencing of other services.


@Component(immediate = true)
public class UserNameFilter implements TextFilter {


private UserLocalService userLocalService;


@Reference
public void addUserLocalService(UserLocalService userLocalService) {
this.userLocalService = userLocalService;
}


public void removeUserLocalService(UserLocalService userLocalService) {
this.userLocalService = userLocalService;
}


Filter (Service) Registry


Registry will track all the extensions, in other words, implementations of the TextFilter interface which are annotated with @Component. It will have up to date list of the extensions when they are deployed and undeployed.


The injection point of the extensions (implementations of the TextFilter) is defined in a method with annotation @Reference so that the container will call this method whenever the extension is deployed. Here is the code snippet.


@Reference (
unbind = "removeTextFilter",
cardinality = ReferenceCardinality.MULTIPLE,
policy = ReferencePolicy.DYNAMIC)
public void addTextFilter(TextFilter textFilter) {
textFilters.add(textFilter);
}


public void removeTextFilter(TextFilter textFilter) {
textFilters.remove(textFilter);
}


Notice the unbind parameter present in the @Reference annotation. It specifies what method need to be called when extension is undeployed. This is optional parameter and if it is not provided, container will be looking for method that has a  prefix “remove” to the interface, here in our case it would be looking for removeTextFilter method.


Accessing OSGi Services from Portlet


We have developed the services and deployed them into the OSGi container. Now finally we need to access them in our portlet so that we can apply the text filter for the annotated screen name.  In Liferay 6.2, portlets are not first class citizens as a components nor services themselves in OSGi container. So they can not participate in the component framework and get the reference to the other components/services. This is like accessing OSGi services from a non OSGi context. In order to achieve this Liferay provided a custom bundle that going to  provide the reference and hide all the implementation details. This is already deployed into OSGi container. ReflectionServiceTracker is an OSGi service to track and inject other available services in the OSGi container with the portlet. We can not use a component framework here as the portlet instance is created by the portal not by OSGi container.


In order to access different services that are deployed into OSGi container, the portlet class need to have ReflectionServiceTracker started in init method and release it in destroy method.


Here is the code snippet


public class FakeChatPortlet extends FreeMarkerPortlet {


private ReflectionServiceTracker reflectionServiceTracker;


@Override
   public void init() throws PortletException {
           super.init();
           reflectionServiceTracker = new ReflectionServiceTracker(this);
   }
   
   @Override
   public void destroy() {
           super.destroy();
           if (reflectionServiceTracker != null) {
                   reflectionServiceTracker.close();
           }
   }


That is all we need to do . So we have now 2 jar files and 1 wab file. Just copy them to data/osgi/bundles and liferay will deploy them. Now you can have option to enable and disable the text filter from the OSGi console. If you stop the service, text filter will be disabled and just screen name will be printed in the list. Is you start the service, screen name will be replaced with the First Name and Last Name  

1 comment:

  1. I really appreciate information shared above. It’s of great help. If someone want to learn Online (Virtual) instructor lead live training in liferay, kindly contact us http://www.maxmunus.com/contact
    MaxMunus Offer World Class Virtual Instructor led training on liferay. We have industry expert trainer. We provide Training Material and Software Support. MaxMunus has successfully conducted 100000+ trainings in India, USA, UK, Australlia, Switzerland, Qatar, Saudi Arabia, Bangladesh, Bahrain and UAE etc.
    For Demo Contact us.
    Nitesh Kumar
    MaxMunus
    E-mail: nitesh@maxmunus.com
    Skype id: nitesh_maxmunus
    Ph:(+91) 8553912023
    http://www.maxmunus.com/


    ReplyDelete