Liferay/OSGi Annotations - What they are and when to use them

When you start reviewing Liferay 7 CE/Liferay DXP code, you run into a lot of annotations in a lot of different ways.  They can all seem kind of overwhelming when you first happen upon them, so I thought I'd whip up a little reference guide, kind of explaining what the annotations are for and when you might need to use them in your OSGi code.

So let's dive right in...

@Component

So in OSGi world this is the all important "Declarative Services" annotation defining a service implementation.  DS is an aspect of OSGi for declaring a service dynamically and has a slew of plumbing in place to allow other components to get wired to the component.

There are three primary attributes that you'll find for this annotation:

  • immediate - Often set to true, this will ensure the component is started right away and not wait for a reference wiring or lazy startup.
  • properties - Used to pass in a set of OSGi properties to bind to the component.  The component can see the properties, but more importantly other components will be able to see the properties too.  These properties help to configure the component but also are used to support filtering of components.
  • service - Defines the service that the component implements.  Sometimes this is optional, but often it is mandatory to avoid ambiguity on the service the component wants to advertise.  The service listed is often an interface, but you can also use a concrete class for the service.

When are you going to use it?  Whenever you create a component that you want or need to publish into the OSGi container.  Not all of your classes need to be components.  You'll declare a component when code needs to plug into the Liferay environment (i.e. add a product nav item, define an MVC command handler, override a Liferay component) or to plug into your own extension framework (see my recent blog on building a healthcheck system).

@Reference

This is the counterpart to the @Component annotation.  @Reference is used to get OSGi to inject a component reference into your component. This is a key thing here, since OSGi is doing the injection, it is only going to work on an OSGi @Component class.  @Reference annotations are going to be ignored in non-components, and in fact they are also ignored in subclasses too.  Any injected references you need, they must be done in the @Component class itself.

This is, of course, fun when you want to define a base class with a number of injected services; the base class does not get the @Component annotation (because it is not complete) and @Reference annotations are ignored in non-component classes, so the injection will never occur.  You end up copying all of the setters and @Reference annotations to all of the concrete subclasses and boy, does that get tedious.  But it is necessary and something to keep in mind.

Probably the most common attribute you're going to see here is the "unbind" attribute, and you'll often find it in the form of @Reference(unbind = "-") on a setter method. When you use a setter method with @Reference, OSGi will invoke the setter with the component to use, but the unbind attribute indicates that there is no method to call when the component is unbinding, so basically you're saying you don't handle components disappearing behind your back.  For the most part this is not a problem, server starts up, OSGi binds the component in and you use it happily until the system shuts down.

Another attribute you'll see here is target. Target is used as a filter mechanism; remember the properties covered in @Component? With the target attribute, you specify a query that identifies a more specific instance of a component that you'd like to receive.  Here's one example:

@Reference(
  target = "(javax.portlet.name=" + NotificationsPortletKeys.NOTIFICATIONS + ")",
  unbind = "-"
)
protected void setPanelApp(PanelApp panelApp) {
  _panelApp = panelApp;
}

The code here wants to be given an instance of a PanelApp component, but it's looking specifically for the PanelApp component tied to the notifications portlet.  Any other PanelApp component won't match the filter and won't be applied.

There are some attributes that you will sometimes find here that are pretty important, so I'm going to go into some details on those.

The first is the cardinality attribute.  The default value is ReferenceCardinality.MANDITORY, but other values are OPTIONAL, MULTIPLE, and AT_LEAST_ONE. The meanings of these are:

  • MANDITORY - The reference must be available and injected before this component will start.
  • OPTIONAL - The reference is not required for the component to start and will function w/o a component assignment.
  • MULTIPLE - Multiple resources may satisfy the reference and the component will take all of them, but like OPTIONAL the reference is not needed for the component to start.
  • AT_LEAST_ONE - Multiple resources may satisfy the reference and the component will take all of them, but at least one is manditory for the component to start.

The multiple options allow you to get multiple calls with references that match.  This really only makes sense if you are using the @Reference annotation on a setter method and, in the body of the method, were adding to a list or array.  Alternatives to this kind of thing would be to use a ServiceTracker so you wouldn't have to manage the list yourself.

The optional options allow your component to start without an assigned reference.  This kind of thing can be useful if you have a scenario where you have a circular reference issue: A references B which references C which references A.  If all three use REQUIRED, none will start because the references cannot be satisfied (only started components can be assigned as a reference).  You break the circle by having one component treat the reference as optional; then they will be able to start and references will be resolved.

The next important @Reference attribute is the policy.  Policy can be either ReferencePolicy.STATIC (the default) or ReferencePolicy.DYNAMIC.  The meanings of these are:

  • STATIC - The component will only be started when there is an assigned reference, and will not be notified of alternative services as they become available.
  • DYNAMIC - The component will start when there is reference(s) or not, and the component will accept new references as they become available.

The reference policy controls what happens after your component starts when new reference options become available.  For STATIC, new reference options are ignored and DYNAMIC your component is willing to change.

Along with the policy, another important @Reference attribute is the policyOption.  This attribute can be either ReferencePolicyOption.RELUCTANT (the default) or ReferencePolicyOption.GREEDY.  The meanings of these are:

  • RELUCTANT - For single reference cardinality, new reference potentials that become available will be ignored.  For multiple reference cardinality, new reference potentials will be bound.
  • GREEDY - As new reference potentials become available, the component will bind to them.

Whew, lots of options here, so let's talk about common groupings.

First is the default, ReferenceCardinality.MANDITORY, ReferencePolicy.STATIC and ReferencePolicyOption.RELUCTANT.  This summarizes down to your component must have only one reference service to start and regardless of new services that are started, your component is going to ignore them.  These are really good and normal defaults and promote stability for your component.

Another common grouping you'll find in the Liferay source is ReferenceCardinality.OPTIONAL or MULTIPLE, ReferencePolicy.DYNAMIC and ReferencePolicyOption.GREEDY.  In this configuration, the component will function with or without reference service(s), but the component allows for changing/adding references on the fly and wants to bind to new references when they are available.

Other combinations are possible, but you need to understand impacts to your component.  After all, when you declare a reference, you're declaring that you need some service(s) to make your component complete.  Consider how your component can react when there are no services, or what happens if your component stops because dependent service(s) are not available. Consider your perfect world scenario as well as a chaotic nightmare of redeployments, uninstalls, service gaps and identify how your component can weather the chaos.  If you can survive the chaos situation, you should be fine in the perfect world scenario.

Finally, when do you use the @Reference annotation?  When you need service(s) injected into your component from the OSGi environment.  These injections can come from your own module or from other modules in the OSGi container.  Remember that @Reference only works for OSGi components, but you can change into a component with an addition of the @Component reference.

@BeanReference

This is a Liferay annotation used to inject a reference to a Spring bean from the Liferay core.

@ServiceReference

This is a Liferay annotation used to inject a reference from a Spring Extender module bean.

Wait! Three Reference Annotations? Which should I use?

So there they are, the three different types of reference annotations.  Rule of thumb, most of the time you're going to want to just stick with the @Reference annotation.  The Liferay core Spring beans and Spring Extender module beans are also exposed as OSGi components, so @Reference should work most of the time.

If your @Reference isn't getting injected or is null, that will be sign that you should use one of the other reference annotations.  Here your choice is easy: if the bean is from the Liferay core, use @BeanReference, but if it is from a Spring Extender module, use the @ServiceReference annotation instead.  Note that both bean and service annotations will require your component use the Spring Extender also.  For setting this up, check out any of your ServiceBuilder service modules to see how to update the build.gradle and bnd.bnd file, etc.

@Activate

The @Activate annotation is OSGi's equivalent to Spring's InitializingBean interface.  It declares a method that will be invoked after the component has started.

In the Liferay source, you'll find it used with three primary method signatures:

@Activate
protected void activate() {
  ...
}
@Activate
protected void activate(Map<String, Object> properties) {
  ...
}
@Activate
protected void activate(BundleContext bundleContext, Map<String, Object> properties) {
  ...
}

There are other method signatures too, just search the Liferay source for @Activate and you'll find all of the different variations. Except for the no-argument activate method, they all depend on values injected by OSGi.  Note that the properties map is actually your properties from OSGi's Configuration Admin service.

When should you use @Activate? Whenever you need to complete some initialization tasks after the component is started but before it is used.  I've used it, for example, to set up and schedule Quartz jobs, verify database entities, etc.

@Deactivate

The @Deactivate annotation is the inverse of the @Activate annotation, it identifies a method that will be invoked when the component is being deactivated.

@Modified

The @Modified annotation marks the method that will be invoked when the component is modified, typically indicating that the @Reference(s) were changed.  In Liferay code, the @Modified annotation is typically bound to the same method as the @Activate annotation so the same method handles both activation and modification.

@ProviderType

The @ProviderType comes from BND and is generally considered a complex concern to wrap your head around.  Long story greatly over-simplified, the @ProviderType is used by BND to define the version ranges assigned in the OSGi manifest in implementors and tries to restrict the range to a narrow version difference.

The idea here is to ensure that when an interface changes, the narrow version range on implementors would force implementors to update to match the new version on the interface.

When to use @ProviderType? Well, really you don't need to. You'll see this annotation scattered all through your ServiceBuilder-generated code. It's included in this list not because you need to do it, but because you'll see it and likely wonder why it is there.

@ImplementationClassName

This is a Liferay annotation for ServiceBuilder entity interfaces. It defines the class from the service module that implements the interface.

This won't be an interface you need to use, but at least you'll know why its there.

@Transactional

This is another Liferay annotation bound to ServiceBuilder service interfaces. It defines the transaction requirements for the service methods.

This is another annotation you won't be expected to use.

@Indexable

The @Indexable annotation is used to decorate a method which should result in an index update, typically tied to ServiceBuilder methods that add, update or delete entities.

You use the @Indexable annotation on your service implementation methods that add, update or delete indexed entities.  You'll know if your entities are indexed if you have an associated com.liferay.portal.kernel.search.Indexer implementation for your entity.

@SystemEvent

The @SystemEvent annotation is tied to ServiceBuilder generated code which may result in system events.  System events work in concert with staging and the LAR export/import process.  For example, when a jouirnal article is deleted, this generates a SystemEvent record.  When in a staging environment and when the "Publish to Live" occurs, the delete SystemEvent ensures that the corresponding journal article from live is also deleted.

When would you use the @SystemEvent annotation? Honestly I'm not sure. With my 10 years of experience, I've never had to generate SystemEvent records or modify the publication or LAR process.  If anyone out there has had to use or modify an @SystemEvent annotation, I'd love to hear about your use case.

@Meta

OSGi has an XML-based system for defining configuration details for Configuration Admin.  The @Meta annotations from the BND project allow BND to generate the file based on the annotations used in the configuration interfaces.

Important Note: In order to use the @Meta annotations, you must add the following line to your bnd.bnd file:
-metatype: *
If you fail to add this, your @Meta annotations will not be used when generating the XML configuration file.

@Meta.OCD

This is the annotation for the "Object Class Definition" aspect, the container for the configuration details.  This annotation is used on the interface level to provide the id, name and localization details for the class definition.

When do you use this annotation? When you are defining a Configuration Admin interface that will have a panel in the System Settings control panel to configure the component.

Note that the @Meta.OCD attributes include localization settings.  This allows you to use your resource bundle to localize the configuration name, the field level details and the @ExtendedObjectClassDefinition category.

@Meta.AD

This is the annotation for the "Attribute Definition" aspect, the field level annotation to define the specification for the configuration element. The annotation is used to provide the ID, name, description, default value and other details for the field.

When do you use this annotation? To provide details about the field definition that will control how it is rendered within the System Setings configuration panel.

@ExtendedObjectClassDefinition

This is a Liferay annotation to define the category for the configuration (to identify the tab in the System Settings control panel where the configuration will be) and the scope for the configuration.

Scope can be one of the following:

  • SYSTEM - Global configuration for the entire system, will only be one configuration instance shared system wide.
  • COMPANY - Company-level configuration that will allow one configuration instance per company in the portal.
  • GROUP - Group-level (site) configuration that allows for site-level configuration instances.
  • PORTLET_INSTANCE - This is akin to portlet instance preferences for scope, there will be a separate configuration instance per portlet instance.

When will you use this annotation? Every time you use the @Meta.OCD annotation, you're going to use the @ExtendedObjectClassDefinition annotation to at least define the tab the configuration will be added to.

@OSGiBeanProperties

This is a Liferay annotation used to define the OSGi component properties used to register a Spring bean as an OSGi component. You'll find this used often in ServiceBuilder modules to expose the Spring beans into the OSGi container. Remember that ServiceBuilder is still Spring (and SpringExtender) based, so this annotation exposes those Spring beans as OSGi components.

When would you use this annotation? If you are using Spring Extender to use Spring within your module and you want to expose the Spring beans into OSGi so other modules can use the beans, you'll want to use this annotation.

I'm leaving a lot of details out of this section because the code for this annotation is extensively javadoced. Check it out: https://github.com/liferay/liferay-portal/blob/master/portal-kernel/src/com/liferay/portal/kernel/spring/osgi/OSGiBeanProperties.java

Conclusion

So that's like all of the annotations I've encountered so far in Liferay 7 CE / Liferay DXP. Hopefully these details will help you in your Liferay development efforts.

Find an annotation I've missed or want some more details on those I've included? Just ask.

Blogs
[...] Another option is to use different ReferenceCardinality, ReferencePolicy and ReferencePolicyOption values (see my blog post on annotations, specifically the section on the @Reference annotation).... [...] Read More
Thanks David for the insights, especially the rules about having @Reference annotation on concrete subclasses was helpful.
On a later occasion I stumbled upon a discussion which puts this 'limitation' in wider perspective, with Peter Kriens's opinion:
https://groups.google.com/d/msg/bndtools-users/LI2YyFjwZNY/qymOS6YXzBUJ
Essentially he says he prevented inheritance support for DI because it creates tighter coupling between classes in a hierarchy, and saving only a tiny bit of duplication.

-Geert
While I do respect his position, I'd call bullshit.

As soon as you subclass, there is inherently a "tight coupling", and that's the intent. I mean, you won't go to your Animal heirarchy and subclass Cat to make a Fish class; if you subclass it is because you are coupling yourself to the implementation details of the superclass.

And worse, when you don't necessarily control the superclasses, you can easily get hamstrung. In another blog post I remember talking about subclassing a Liferay class (I think an MVCActionCommand implementation) where the superclass had @Reference right on a private variable declaration. As a subclass, I had to not only mirror the @Reference on a setter method but I also had to use reflection to get access to the superclass field to inject the reference.

It's easy to tear apart most of his arguments, actually. "If you subclass, you must know the class hierarchy", well, duh, isn't that true whether you're doing OSGi or not? Same is true for the "brittle" statement, the "changing superclass can break subclasses", etc. We've seen all of this in Liferay when no annotations or OSGi were part of the picture. That, after all, is the nature of inheritance. When you subclass, you are doing so because you want the bulk of the functionality and, yes, dependencies of the superclass - you might be overriding all or part of that implementation, but that is the subclasser's prerogative.

Now of course the "bullshit" thing is my own opinion, and it is a reflection of my pragmatism and reflection of real-world situations, and it discards the purity one can have when dealing solely with theory. So while Peter is maybe absolutely right in theory, he ends up being wrong in reality.

Personally I think the real reason it is not supported is because honestly there are challenges that would be difficult to overcome, such as how subclasses could change @Reference annotations - if the super class used a generic @Reference but subclass used a more specific filter, how would that be resolved. Or how would a subclass change the cardinality or policy of a superclass. Also how would a subclass "remove" an @Reference dependency that it didn't need or want OSGi to stop loading the component if not satisfied. I get that these would be hard edge cases to solve, but I would have respected a response saying it was easier to discard reference inheritance than it was to solve these more difficult concerns.

Hi David, 

First of all Thank you to post very useful Information.

You clearly rolled out that "@Reference annotations are going to be ignored in non-components, and in fact they are also ignored in subclasses too".

But, I have found @Reference usage in the abstract classe is com.liferay.users.admin.web.internal.frontend.taglib.servlet.taglib.BaseUserScreenNavigationEntry.java

 

puzzling is it working or not there?

But, when I am implementing , getting as null for all @Reference class objects in the abstract class.

A new directive was added to BND, "-dsannotations-options: inherit". When placed in the bnd.bnd file, any @Reference in superclasses will also be processed. When this blog was published, that directive did not yet exist (or at least I was unaware of it if it did).

Greetings Dave - you've managed to write an excellent, comprehensive and very needful article all-in-one! I'm trying to create osgi component exactly like the ImageTool component in Liferay 7. I've implemented my ImageDisplayUtil class and ImageDisplay interface both in util folder in api module and I've also implented my ImageDisplayImpl class in service module. But I notice that Liferay doesnt use the @Component, @Reference annotations for the ImageTool and ImageToolImpl classes (just uses @ProviderType) - & yet these classes work. In my own case - I'm getting a nullpointer exception when trying to get a reference of the interface from the util. I haven't used the @Component or @Reference either. But how come Liferay core classes wo4k without thesr anotts and mine doesnt?Greetings Dave - you've managed to write an excellent, comprehensive and very needful article all-in-one! I'm trying to create osgi component exactly like the ImageTool component in Liferay 7. I've implemented my ImageDisplayUtil and ImageDisplay
Greetings Dave - you've managed to write an excellent, comprehensive and very needful article all-in-one! I'm trying to create osgi component exactly like the ImageTool component in Liferay 7. I've implemented my ImageDisplayUtil class and ImageDisplay interface both in util folder in api module and I've also implented my ImageDisplayImpl class in service module. But I notice that Liferay doesnt use the @Component, @Reference annotations for the ImageTool and ImageToolImpl classes (just uses @ProviderType) - & yet these classes work. In my own case - I'm getting a nullpointer exception when trying to get a reference of the interface from the util. I haven't used the @Component or @Reference either. But how come Liferay core classes wo4k without thesr anotts and mine doesnt?Greetings Dave - you've managed to write an excellent, comprehensive and very needful article all-in-one! I'm trying to create osgi component exactly like the ImageTool component in Liferay 7. I've implemented my ImageDisplayUtil and ImageDisplay

Hi David H,

We need to upgrade our portlets from LR6.2 to LR7.1. This portlets use massively Spring 4.3.22.  I understand, that between modules I should use the DS, but internally I want to stay in Spring. The aim is when activated, the module start spring context as in the past and register additionally the DS services.

I did a couple of test and it seems the Liferay OSGi Spring extender inject on the classpath the Spring 4.1.9 version and there is no way to configure the budle to overload it,  even if you pack directly into a each bundle.

I need to upgrade the Spring to 4.3.19, or even better 5.x. Is there a option to write own extender, which will be applied just for osgi bundles with some Require-Capability´s and the Liferay extender will be skipped there?

Thanks for reply, or whom I can as for?

Petr