Liferay has been slowly integrating an OSGi framework into it's core; not in the sense that Liferay will nessecarily run "within" an OSGi runtime itself (there are arguments for doing that, and it's possible to a certain extent but the gain just isn't in doing that just yet), more that Liferay will "host" and OSGi runtime within itself that Liferay will utterly control.
Why do this? Well, as I've stated many times before, Liferay is a complex beast with an intricate network of features, so many features in fact that the ocassionaly have such indistinct lines such as finding the where one feature ends and another begins can be difficult.
OSGi will hopefully play a part in 1) providing us with a lot of re-use of features we had ourselves partially implemented but for which there existed a fully specified implementation our in the larger java eco system. The savings those existing features can bring is one of many benefits. 2) A specified lifecycle for "modules" (a.k.a. bundles) also is a huge benefit, one we can simply use. 3) Dependency resolution and management is another piece we had started to implement but it was clear to me that this had already been done much more effectively than Liferay alone could implement, again as part of an OSGi specification.
The number of benefits is almost too great to list. However, one of the greatest advantages can't be discussed enough: Modularity.
Modularity is something that Liferay is now learning to embrace. Hopefully in the future we'll see many more examples like the one I'm about to illustrate.
One of the things I spend a lot of time doing is running and debugging code. There are a number of challenges to doing this with such a large application as Liferay. It takes time to get up and running, the vast number of features sometimes makes it tricky to isolate just the right service calls in order to test some specific interaction. I'm most often running the portal in debug mode executed from my IDE (Eclipse). A favourite thing to do is, place a break point and then use the context evaluation to invoke some specific code (using the Display view). However, my main purpose for doing this is to be able to fire some operation often unrelated with the actual breakpoint position. Effectively I just want to execute some arbitrary code in the JVM.
What would be ideal is if I had a command line from which I could do the same thing.
The OSGi echo system provides that out there somewhere a bunch of smart people have specified some behaviors, how to build properly interacting bits of code, and provided I have the appropriate runtime for those bits, I can interconnect them with my own bits and compose a greater feature.
Gogo is an OSGi (hence java) command shell implemented by the Apache Felix community.
"Apache Felix Gogo is a subproject of Apache Felix implementing the OSGi RFC 147, which describes a standard shell for OSGi-based environments."
Installing this shell with Liferay (due to Liferay's new interated OSGi runtime) is almost too simple.
First we'll download the necessary bundles:
(Note: We could have even avoided this pre-download step, but due to a small logical bug in Liferay's integration, we'll have to download the bundles ahead of time. I'll be fixing this bug in the days to come.)
Enable Liferay's Module Framework
Place the following in your
portal-ext.properties (or equivalent props file):
Configure some initial bundles to start when the OSGi runtime starts in Liferay (still in
]$ telnet localhost 11311
You should see something like the following:
We have sheel access!
But what can we do with this?
The first thing I wanted to do was investigate how the shell works and how to customize the configuration.
Passing System Properties to OSGi bundles
One thing I should add is that OSGi bundles often have system properties specific to them, and in order to isolate these from potentially other osgi runtimes in the same JVM, Liferay provided a means of passing these as namespaced portal properties.
An example of this is passing a Gogo specific system property called
gosh.home. And so I added that property to my
portal-ext.properties (prefixed by module.framework.properties.):
This places the Gogo shell's home directory within Liferay's osgi directory inside
Gogo is then trained to look inside an
/etc directory within it's
gosh.home for configuration files.
In this folder I added two files.
gosh_profile (think bash_profile):
motd (a.k.a. Message of the day):
Now a restart and my shell appears like:
The shell offers many build in commands. Executing
help will reveal many of the extra commands namespaced by providers. However these are executable without the namespace (the namespace is used for conflict resolution of two namespaces provide commands of the same name).
A few choice commands are:
ss (short bundle status)
diag (diagnose a problem bundle)
However, it's possible to add commands dynamically as well as use closures. This is where the real power begins.
You may note that we added a custom set of commands based on the java.lang.System class in the gosh_profile:
addcommand system (loadclass java.lang.System)
What this does is loads the class and assigns the public methods of that class under the system prefix. Executing:
outputs all the system properties. In a similar fashion any class can be added as a set of shell commands.
Modularity via OSGi allowed me to easily add functionality not previously available in Liferay. I can now build out some commands that will enable me to quickly take actions which previously required me to find round about ways to achieve.
Well, that's all for today. It was simple and fun to extend Liferay with shell access, I solved my problem. It also demonstrates the power of OSGi since we didn't have to write a single line of code in order to benefit from it's well defined modularity and lifecycle specifications.
I'll talk more about shell commands, closures, and how to add and use custom Liferay commands in more detail in a future blog post. I'll also start using this shell to show how to start building small Liferay extensions using OSGi.
Eventually, more of the Module Framework features (such as the web extender) that Miguel and I have been working on will reach master and we'll start to discuss those as well.