One of the things that I never really used in 6.x was the Liferay upgrade APIs.
Sure, I knew about the Release table and stuff, but it just seemed kind of cumbersome to not only to build out your code but on top of that track your release and support an upgrade process on top of all of that. I mean, I'm a busy guy and once this project is done I'm already behind on the next one.
When you start perusing the Liferay 7 source code, though, one thing you'll notice is that there is upgrade logic all over the place. Pretty much every portlet module includes an upgrade process to support upgrading from version "0.0.0" to version "1.0.0" (this is the upgrade process to change from 6.x to the new 7.x module version).
And you'll even find that some modules include upgrades from versions "1.0.0" to "1.0.1" to support the independent module versioning that was the promise of OSGi.
So now that I'm trying to exclusively build modules, I'm thinking it's an appropriate time to dig into the upgrade APIs and see how they work and how I can incorporate upgrades into my modules.
The New Release
So previously we'd have to manage the Release entity ourselves, but Liferay has graciously taken that over for us. Your bnd.bnd file, where you specify your module version, well that now becomes the foundation of your Release handling. And just like the portal module, an absense of a Release is technically version "0.0.0" so now you can handle first-time deployment stuff too.
The Upgrade API
Before diving into implementation, let's take a little time to look over some of the classes and interfaces Liferay provides as part of the Upgrade API. We'll start with the classes from the com.liferay.portal.kernel.upgrade package:
| Name | Purpose |
|---|---|
| UpgradeStep | This is the main interface that must be implemented for all upgrade logic. When registering an upgrade, an ordered list of UpgradeSteps are provided and the upgrade process will execute these in order to complete an upgrade. |
| DummyUpgradeStep | The simplest of all concrete implementations of the UpgradeStep interface, this upgrade step does nothing. But it is a useful step to use for handling new deployments. |
| UpgradeProcess | This is a handy abstract base class to use for all of your upgrade steps. It implements the UpgradeStep interface and has support for database-specific alterations should you need them. |
| Base* | These are abstract base classes for upgrade steps typically used by the portal for managing upgrades from portlet wars to new module-based portlets. For example, the BaseUpgradePortletId class is used to support fixing the portlet ids from older id-based portlet names to the new OSGi portlet ids based on class name. These classes are good foundations if you are building an upgrade process to move your own portlets from wars to bundles or want to handle upgrades from 6.x compatibility to 7.x. |
| util.* | For those wanting to support a database upgrade, the com.liferay.portal.kernel.upgrade.util package contains a bunch of support classes to assist with altering tables, columns, indexes, etc. |
Registering The Upgrade
All upgrade definitions need to be registered. That's pretty easy, of course, when one is using OSGi. To register an upgrade, you just need a component that implements the UpgradeStepsRegistrator interface.
But first a word about code structure...
So Liferay's recommendation is to use a java package to contain all of your upgrade code, typically in a package named upgrade, is part of your portlet web module, and the package is at the same level as your portlet package (if you have one).
So if your portlet code is in com.example.myapp.portlet, you're going to have a com.example.myapp.upgrade package.
In here you'll have sub-packages for all upgrade versions supported, so you might have "v1_0_0" and "v1_0_1", etc. Upgrade step implementations will be in the subpackage for the upgrade level they support.
So now we have enough details to start building out the upgrade definition. Start by updating your build.gradle file to introduce a new dependency:
compileOnly group: "com.liferay", name: "com.liferay.portal.upgrade", version: "2.3.0"
This pulls in some utility classes we'll be using below.
Let's assume we're building a brand new module and just want to get a placeholder upgrade definition in place. This is quite easily done by adding a single component to our project:
@Component(immediate = true, service = UpgradeStepRegistrator.class)
public class ExampleUpgradeStepRegistrator implements UpgradeStepRegistrator {
@Activate
protected void activate(final BundleContext bundleContext) {
_bundleName = bundleContext.getBundle().getSymbolicName();
}
@Override
public void register(Registry registry) {
// for first time deployments this will start by creating the initial release record
// with the initial version of 1.0.0.
// Also use the dummy upgrade step since we're not doing anything in this upgrade.
registry.register(_bundleName, "0.0.0", "1.0.0", new DummyUpgradeStep());
}
private String _bundleName;
}
So that's pretty much it. Including this class in your component will result in it registering as a Release with version 1.0.0 and you have nothing else to worry about.
When you're ready to release verison 1.1.0 of your component, things get a little more fun.
In your v1_1_0 package you'll create classes that implement the UpgradeStep interface typically by extending the UpgradeProcess abstract base class or perhaps a more appropriate class from the above table. Either way you'll define separate classes to handle different aspects of the upgrade.
We'd then come back to the UpgradeStepRegistrator implementation to add the upgrade steps by including another registry call:
registry.register(_bundleName, "1.0.0", "1.1.0", new UpgradeMyTableStep(), new UpgradeMyDataStep(), new UpgradeMyConfigAdmin());
When processing this upgrade definition, the Upgrade service will invoke the upgrade steps in the order provided. So obviously you should take care to order your steps such that they can succeed given only what steps have been processed before and not on subsequent steps.
Database Upgrades
So one of the common issues with Service Builder modules is that the tables will be created when you first deploy the module to a new environment, but updates will not be processed. I think we could argue on one side that it is a bug or on the other side that expecting Service Builder to track data model changes is far outside of the tool's responsibility.
I'm not going to argue it either way; we are where we are, and solving from this point is all I'm really worried about.
As I previously stated, the com.liferay.portal.kernel.upgrade.UpgradeProcess is going to be the perfect base class to accommodate a database update.
UpgradeProcess extends com.liferay.portal.kernel.dao.db.BaseDBProcess which brings the following methods:
- hasTable() - Determines if the listed table exists.
- hasColumn() - Determines if the table has the listed column.
- hasColumnType() - Determines if the listed column in the listed table has the provided type.
- hasRows() - Determines if the listed table has rows (in order to provide logic to migrate data during an upgrade).
- runSQL() - Runs the given SQL statement against the database.
UpgradeProcess itself has two upgradeTable() methods both of which add a new table to the database. The difference between the two, one is simple and will create a table based on the name and a multidimensional array of column detail objects, the second one has additional arguments for fixed SQL for the table, indexes, etc.
Additionally UpgradeProcess has a number of inner support classes to facilitate table alterations:
- AlterColumnName - A class to encapsulate details to change a column name.
- AlterColumnType - A class to encapsulate details to change a column type.
- AlterTableAddColumn - A class to encapsulate details to add a new column to a table.
- AlterTableDropColumn - A class to encapsulate details to drop a column from a table.
Let's write a quick upgrade method to add a column, change another column's name and another column's type. To facilitate this, our class will extend UpgradeProcess and will need to implement a doUpgrade() method:
public void doUpgrade() throws Exception {
// create all of the alterables
Alterable addColumn = new AlterTableAddColumn("COL_NEW");
Alterable fixColumn = new AlterColumnType("COL_NEW", "LONG");
Alterable changeName = new AlterColumnName("OLD_COL_NAME", "NEW_COL_NAME");
Alterable changeType = new AlterColumnType("ENTITY_PK", "LONG");
// apply the alterations to the MyEntity Service Builder entity.
alter(MyEntity.class, addColumn, fixColumn, changeName, changeType);
// done
}
So the alterations are based on your ServiceBuilder entity but otherwise you don't have to worry much about SQL to apply these kinds of alterations to your entity's table.
Conclusion
Using just what has been provided here, you can integrate a smooth and automatic upgrade process into your modules, including upgrading your Service Builder's entity backing tables since SB won't do that for you.
Where can you find more details on doing some nitty-gritty upgrade activities? Why, the Liferay source of course. Here's a fairly complex set of upgrade details to start your review: https://github.com/liferay/liferay-portal/tree/master/modules/apps/knowledge-base/knowledge-base-service/src/main/java/com/liferay/knowledge/base/internal/upgrade
Enjoy!
Postscript
My good friend and coworker Nathan Shaw forwarded me a reference that I think is worth adding here. Thanks Nathan!

