Managing Liferay through the Gogo shell

I have done a huge refactor for most of our OSGi related work, moving the majority of its components to the Liferay plugins SDK. Everything except the graphical admin UI is already in the master branch of the official repo so you can play with it; we would love to hear your feedback.

My goal with this post is to show you how create a new OSGi bundle in the plugins SDK using a practical example: extending the OSGi shell (we have spoken about it sometime ago). Let's try to do it:

Basic project structure

Currently there is no target in the SDK which allows you to create a bundle (I will push it soon) so you can use this example (or the other modules as http-service-shared or log-bridge-shared) as the basic skeleton for your tests:

Basic OSGi bundle skeleton

As you can see, the structure is very simple; let my try to highlight the main OSGi related points:

  • All the new OSGi plugins will be based in the great bnd tool . You can configure all your bundle requirement through the bnd.bnd file
  • The build.xml of the plugin must import the build-common-osgi-plugin.xml build file: <import file="../../build-common-osgi-plugin.xml" /> 

As I have told you before I will include an Ant target to create this basic skeletons, but, until then, you need to do this by hand. Sorry about that :(.

Writing Gogo shell custom commands

As we have stated at the beginning of the post, we are going to write a bunch of custom commands for the Gogo shell. As you will see, this is an extremely easy task.

This commands can be created using a simple OSGi service with a couple of custom properties. We will use the OSGi declarative services approach in order to register our commands (Liferay already includes an implementation of the Declarative Services specification in the final bundle, so you don't need to deploy it by yourself).

At this point, we are ready to write our first command: the ListUsersCommand: 

The @Component annotation let us to define a new OSGi service in an extremely easy way. The Gogo commands are registered in the console using the properties osgi.command.function and the osgi.command.scope. The first one establishes the name of the namespace where the command will be allocated (in order to prevent name collisions) while the latter specifies the name of the command. It is important to note that the Declarative Service annotations I am using does not support inheritance, so everything you declare in a base class will not be inherited down in the hierarchy.

And, how do I write the implementation of my new command? You just need to write a new public method named as the value written in the osgi.command.function. If your command needs some argument you will need to specify as a parameter of your method (an the console will do the coercion of the basic types). In our example we are creating a usermanagment namespace, with a command called listByCompany which expects the companyId (long) as the unique parameter.

Easy, isn't it?

Consuming Liferay services

I would like to highlight the implementation of the listByCompany command (I am sure you have already guessed the previous command retrieves all the users of a certain company).

In order to get all this info we need to call the corresponding method in the users service. We could do something like UserLocalServiceUtil.getCompanyUsers(companyId) but this is not a good approach, so we are to going to get all the benefits of having all the Liferay services as OSGi services. We just need to grab a reference to the UserLocalService bean:

 
  
@Reference
public void setUserLocalService(UserLocalService userLocaService) {
   _userLocalService = userLocaService;
}
 
protected UserLocalService _userLocalService;
 

Building and running the bundle

Once we have written our command we need to build our final artifact: just type ant jar in the project folder and you will get the final jar file into the dist folder. Before deploying our new bundle let's connect to the Gogo shell in order to check which bundles we have already installed:
 
 
As you see, we have already a bunch of bundles running but nothing about the new commands we are writing. What commands are currently available in the console? Just type help in the console
 
Gogo shell available commands
 
Now we have to deploy our new bundle. To do that, we just need to copy it to the folder $liferay.home/data/osgi/deploy (it is the default folder, you can change it in the properties). You can use the deploy Ant task our just copy the folder into the previous folder. Once the bundle is deployed we should see it in the bundles list:
 
Available bundles once the custom command has been installed
 
Take a look to the last line; you will see our bundle has been deployed and it is, hopefully, running. Let's see which commands we do have available once our new bundle is already installed.
 
 
Do you see the last two lines? These are the commands we have written (in this post we only have described one command, you can find the other one in the companion source code). So, if I type listByCompany ID_OF_A_COMPANY (mine is 10153) (since there is no collision I don't need to prefix the command with the namespace) you should get an output similar to this:
 
Users of the company 10153
    User default@liferay.comwith id 10157
    User test@liferay.comwith id 10195
 
Along this post we have seen how we can extend the Gogo shell creating new commands, consuming Liferay services and building a basic OSGi bundle in the plugins SDK. It is not a big deal but I think it is a good starting point to get used to the new OSGi abilities. Hopefully I will be able to push some more complex examples in the near future.
 
You can find the whole source code in the shared/gogo-commands-shared folder at my plugins repo.