Blogs

Overriding Component Properties

Introduction

So once you've been doing some Liferay OSGi development, you'll recognize your component properties stanza, most commonly applied to a typical portlet class:

@Component(
	immediate = true,
	property = {
		"com.liferay.portlet.add-default-resource=true",
		"com.liferay.portlet.display-category=category.hidden",
		"com.liferay.portlet.layout-cacheable=true",
		"com.liferay.portlet.private-request-attributes=false",
		"com.liferay.portlet.private-session-attributes=false",
		"com.liferay.portlet.render-weight=50",
		"com.liferay.portlet.use-default-template=true",
		"javax.portlet.display-name=my-controlpanel Portlet",
		"javax.portlet.expiration-cache=0",
		"javax.portlet.init-param.template-path=/",
		"javax.portlet.init-param.view-template=/view.jsp",
		"javax.portlet.name=" + MyControlPanelPortletKeys.MyControlPanel,
		"javax.portlet.resource-bundle=content.Language",
		"javax.portlet.security-role-ref=power-user,user",
		"javax.portlet.supports.mime-type=text/html"
	},
	service = Portlet.class
)
public class MyControlPanelPortlet extends MVCPortlet {
}

This is the typical thing you get when you use the Blade tools' "panel-app" template.

This is well and good, you're in development and you can edit these as you need to add, remove or change values.

But what can you do with the OOTB Liferay components, the ones that are compiled into classes packaged into a jar which is packaged into an LPKG file in the osgi/marketplace folder?

Overriding Component Properties

So actually this is quite easy to do. Before I show you how, though, I want to show what is actually going on...

So the "property" stanza or the lesser-used "properties" one (this one is used to identify a file to use for component properties), these are actually managed by the OSGi Configuration Admin service. Because it is managed by CA, we actually get a slew of functionality without even knowing about it.

The @Activate and @Modified annotations that allow you to pass in the properties map? CA is participating in that.

The @Reference annotation target filters referring to property values? CA is participating in that.

Just like CA is at the core of all of the configuration interfaces and Liferay's ConfigurationProviderUtil to fetch a particular instance, these properties can be accessed in code in a similar way.

The other thing that CA brings us, the thing we're going to take advantage of here, is that CA can use override files w/ custom property adds/updates (sorry, no deletes).

Lets say my sample class is actually com.dnebinger.control.panel.test.internal.portlet.MyControlPanelPortlet. To override the properties, I just have to create an osgi/configs/com.dnebinger.control.panel.test.internal.portlet.MyControlPanelPortlet.cfg file. Note the importance of a) the location where the file goes, b) the file name is the full package/class name and c) the file has either the .cfg or .config extension and conforms to the appropriate CA formatting for the type.

The .cfg format is the simplest of the two, it actually follows a standard property file format. So if I wanted to override the category, to expose this portlet so it can be dropped on a page, I could put the following in my osgi/configs/com.dnebinger.control.panel.test.internal.portlet.MyControlPanelPortlet.cfg file:

com.liferay.portlet.display-category=category.sample

That's all there is to it. CA will use this override when collecting the component properties and, when Liferay is processing it, will treat this portlet as though it is in the Sample category and allow you to drop it on a page.

In a similar way you can add new properties, but the caveat is that the code must support them. For example, the MyControlPanelPortlet is not instanceable; I could put the following into my .cfg file:

com.liferay.portlet.instanceable=true

I'm adding a new property, one that is not in the original set of properties, but I know the code supports it and will make the portlet instanceable.

Conclusion

Using this same technique, you can override the properties for any OOTB Liferay component, including portlets, action classes, etc.

Just be sure to put the file into the osgi/configs folder, name the file correctly using full path/class, and use the .cfg or .config extensions with the correct format.

You can find out more about the .config format here: https://dev.liferay.com/discover/portal/-/knowledge_base/7-0/understanding-system-configuration-files

Great post David, really useful. Unfortunately, the not fully OSGi Portlet registration method (that involves the Demoticon makes it quite hard to override an existing portlet component with standard methods (with service ranking for example). But fortunately, the migration of portlet properties from xml files to OSGi properties, together with the MVCCommand class system, make almost all aspects of Portlets customizable in a really easy and flexible way
I'm not sure I'm following. Do you have an example of something that is hard to override in an existing portlet?
Sorry, I will try to explain better the concept, but it's not strictly related to your article. The part related to your post was the importance of being able to correctly override portlet properties, that is a really useful feature of the new OSGi-based portlets in Liferay 7.0 ^_^.

The thing that is hard to override in an existing portlet is...the portlet itself. Portlets are OSGi components, so one might expect that it's possible to override an existing Portlet class by specifying another portlet class with the same javax.portlet.name and an higher service rank. Well, since portlet service registration inside Liferay involves the DB, Portlet service registration is not fully OSGi. There are two main reasons for that:
- I cannot have two portlet components with the same javax.portlet.name contemporary loaded inside the system (while normally I can have multiple implementations of the same OSGi service, and each reference will resolve it's implementation according to service ranking or more sophisticated OSGi filters)
- If I have two portlets with the same javax.portlet.name property, one of the two will throw an error, the related PortletModel will not be loaded inside the DB and the service will be discarded by the PortletTracker. But which portlet will be actually loaded will not depend on OSGi filters or service rankings: the first module processed by the sistem will win. It's only a matter of time.

According to that, overriding a Portlet module this way is DEFINITELY a wrong way to proceed. And this is what I meant with "it's hard to override an existing portlet component with standard methods". But fortunately, we have MVCCommands and Component properties, so we can easily override the 99% of a portlet behaviour ^^
Yep, that is true. I think the idea is that with the other extension points, you shouldn't have to override the portlet itself. I'm not sure I agree, that sometimes it may be the only way to affect a particular change. Which is why I wrote the blog about extending OSGi modules: https://web.liferay.com/web/user.26526/blog/-/blogs/extending-liferay-osgi-modules

Thank you for the interesting blog post, David. However in most of our environments we do not have direct access to the filesystem, so it is not possible to put any files directly into osgi/configs.

 

So I wondered if it is possible to write a deployable plugin which overrides component properties. I played around a litte with the com.liferay.portal.configuration.extender.internal.ConfiguratorExtender (as described in one of your other posts at https://community.liferay.com/en/blogs/-/blogs/meet-the-extenders). 

 

By using such a ConfigurationExtender it seems to be possible to deploy a bundle which overrides the configuration of a component. A small caveat is that the configuration is only overriden once. That means if a configration already exists, the configuration is not overridden. 

Thanks, I just used this to 'fragment enable' an OOTB Liferay Widget with the com.liferay.fragment.entry.processor.portlet.alias property.

Once again you have the answer... I had a ticket open for this and it has stood still for a week with them saying 'not sure how are you trying to do it?' instead of saying this is how you do it - I referred them to your blog.

we now have per layout portlet preferences for the calendar portlet. (which we are extending the UI to show / hide a number of elements. we were trying to override the MVCPortlet class though and that never was able to compile and deploy.

com.liferay.portlet.calendar.web.portlet.CalendarPortlet.java  should we be able to overwrite/extend that some how - we may need to add more serveResource methods and our own routes.xml to handle ics subscriptions and ical downloads to outlook.

 

But the old liferay portlet properties can now be overwritten - I even moved the category for Add portlet.

 

Thanks

 

 

Hi David,

Thanks for great post. We have similar requirements as we need to place 'My Workflow Tasks' portlet under sample category so admin can place this portlet on any pages.

 

I followed this blog and I:

1) Placed a config file named as 'com.liferay.portal.workflow.task.web.internal.portlet.MyWorkflowTaskPortlet.cfg' inside osgi/configs folder,

2) Placed following property in the cfg file mentioned above: com.liferay.portlet.display-category=category.sample

3) Cleaned/Restarted server.

 

Unfortunately, 'My Workflow Tasks' portlet is still not available under 'sample' category.

 

Seems I am missing something.

 

Can you please let me know how can we achieve this in Liferay 7.0 ?