However none of these resources offer much guidance on the subject of organizing a large project that consists of multiple plugins. In this post i'll try and document some (best ?) practices that I distilled from working on a Liferay project that uses the Liferay sdk.
Now before I start, let me first say that I definitely would not recommend using the Liferay sdk for all types of projects. In fact in many cases you will be better served by the liferay maven plugin
The liferay sdk has many deficiencies such as having no support for unit testing(!) and not supporting libraries that can be shared across different plugin modules. However currently the liferay sdk is more mature than the maven plugin is, has more functionality, and most if not all available documentation references the sdk.
As a rule of thumb : if your project integrates tightly with Liferay's built in services and your plugins are not very complex, then using the standard liferay stack : Liferay sdk / MVCPortlet / servicebuilder is a viable option. If your application is more complex then you'll be a lot happier with a more general purpose stack like maven / spring portlet mvc / jpa
But I digress. On with the best practices
1. Have a single build file that sets up your development environment
When i got into working with Liferay, Liferay 5.2.x was the current release and setting up a development environment was a 24 step process. We documented these steps on the wiki and on average it would take a new developer on the project a full day to get up and running. It was obvious that something needed to change. We ended up creating an ant script that automated most of those 24 steps which drastically reduced the ramp up time.
2. Build Liferay portal from source and apply patches
One of the biggest drawbacks of Liferay is that it's buggy, very buggy, and worst of all, unless you purchase the enterprise edition, you will not get any bugfixes including security fixes(!) but even when you purchase a license you are likely to run into issues that are both critical and cannot be worked around. So from the get go assume you will have to apply patches to the portal and adjust your development practices accordingly. This does not mean btw that i am suggesting placing the liferay sources under version control. What I do is, i zip up the liferay sources and place them on a webserver accessible from the local network. As part of the build , our build script will download the sources from this webserver, unzip and build them after applying the patches we require. Why am I suggesting you use patches and am i not suggesting that you for instance place these fixes in an ext module? Much of Liferay's internal modules are based on service builder, when there's a bug in servicebuilder then all of the modules based on servicebuilder need to be "regenerated" So a single change in service builder can result in changes to hundreds if not thousands of files. Having this fix as a single patch is much more managable. Also different application servers load classes in different order. So if you intended to overwrite a class in the portal by creating a class with the same name in an ext module then this might work on one server but fail on another
3. Configure your sdk
When you do "idiomatic" liferay development then you generate your data access and service layers using servicebuilder Unless you change your sdk configuration, all the code servicebuilder generates will include the liferay copyright statement at the top of each generated source file. But worst of all the generated code will use tabs, not spaces. Since everybody knows tabs are evil (right? ) you should fix that straight away by editing your misc/jalopy.xml file
4. During development run tomcat in debug mode
If you run Liferay on tomcat you can issue the following command to start liferay in debug mode
./catalina.sh jpda run
or if you use windows
catalina.bat jpda run
This will let you attach a remote debugger on port 8000. I can't begin to tell you how useful this is when tracking down a bug in your application
5. During development run Liferay in development mode
Adding -Dexternal-properties=portal-developer.properties to the vm arguments in setenv.sh / setenv.bat will enable development mode which optimizes Liferay Portal for development by removing all caches and thus making sure that any change made by the developer is visible in the website as soon as technically possible.
6. Define your dependencies in liferay-plugin-package.properties
Often when developing a plugin you will need addtional libraries. You can place these libraries in the WEB-INF/lib folder of your plugin but if the dependency you need is used by liferay as an internal dependency then you can reference this library from your liferay-plugin-package.properties file and it will be copied to your WEB-INF/lib folder when your portlet is deployed. This means you do not have to include these jars in your plugin. So your deployable will be small
7.Check the Liferay sdk in into version control
In essence the plugin sdk is just a bunch of ant scripts and associated configuration. It is something that liferay provides and it changes between different versions of the Liferay platform. It is something that you as a developer intuitively do not want to check into your project. But you should do it anyway I put a fair bit of effort into trying to somehow externalize it. But in the end it just was more practical to check it all in, at the cost of making upgrading to a new version of the sdk harder.
8. Check your ide files into version control
This kind of goes against common wisdom and is sure to piss off developers using alternative ide's, but, because the liferay sdk is based on ant, not maven, there is no easy way to import a liferay sdk project in your IDE*. Setting up all the libraries, tag lib descriptors, xml schema's, formatting rules etc, takes a lot of time, so do it once and check in your files. If you use Intellij idea (which you should) that means checking in all the files in your .iml files and .idea folder (excluding workspace.xml and tasks.xml which store user specific settings) Also make sure you attach the liferay sources to your project. You will be using them a lot when debugging or trying to figure out what a method on a liferay class does
*If you use eclipse you might be able to use the setup-eclipse target in the sdk. Also Liferay IDE might make some things easier. But still to me succeeding in setting up a project in eclipse is much akin to finishing first at the special olympics. Even if you win you are still retarded (flames to /dev/null please )
9.Check your servicebuilder artifacts into into version control
When you generate a new data access and service layer in using servicebuilder it will generate a bunch of files for you. There are the java files that make up the interfaces that go into WEB-INF/service. You should never edit these by hand. Then there are the implementation specific files that go into WEB-INF/src, some of which you can change and which influence the
interfaces generated when you regenerate the service. And finally there is the service jar that gets added to WEB-INF/lib it holds the compiled WEB-INF/service files. You will have to check all these files into version control, even the jar containing the compiled classes(!) Early on I experimented with making running ant build-service part of the build so that it would run everytime you build a portlet. That way i would not have to check in the jar file. This did not work out. Every time you run "ant build-service" it will update the build.number property in src/service.properties. When you deploy a service builder service, liferay will use this property to determine if this service is newer than the one currently deployed and if, by extension, it needs to run database update scripts. If you make running ant build-service part of the build then your service.properties will constantly be dirty and you'll end up with lots of conflicts when working on a project with multiple developers. Exclude your service.properties file from version control and your database updates won't run on production.
10. When in Rome
If like me you are an elitist jerk who snubs at liferay code this will be the hardest pill to swallow. Liferay is constructed using a rather unusual design where your data and service layers are generated from an xml file. These same services are then accessed via static utility methods. Often from within scriptlets in a jsp(!). Portlet's you develop using the sdk are expected to follow the same patterns. In the past I've tried fighting it but you will invariably run into trouble when you do. Things like the liferay tag libraries, that ensure that all liferay portlets share a common look and feel are just not written with use without scriptlets in mind. And if you implement an AssetRenderer your render method is expected to return a path to a jsp and not a url to an action etc. If your portlets are simple enough, just do as the romans do.
11. Extend built-in portlets judiciously
Liferay lets you customize practically any aspect of the portal. But that does not mean you *should* change any aspect of the portal. Resist the urge to add just one more hack or to change built-in portlets in too significant a way. Large components like the document library and calendar have significantly changed in the last release and are likely to change again. If you made changes to these components in 6.0.x release you'll likely have to put in a fair bit of work before you can upgrade to the 6.1 release. Try keeping your changes to a minimum and when you do make
changes document them well. For instance if you use jsp hooks, check in the original jsp file first, then commit your changes on top with a meaningful comment. That way the person doing the upgrade can simply do a diff between the original and the hooked file to see what was changed.
12. Do continuous integration
13. The complete portal (liferay + plugins) should be your deployment artifact
Deployment should be both straightforward and reproducable. Our continuous build produces a single deployment artifact which is a tar.gz file that contains tomcat with liferay installed on top and all the plugins we developed ready in the deploy folder, to be deployed on first startup
Deploying a new release now looks like this :
rm -rf /usr/local/liferay/apache-tomcat-6.0.26
rm -rf /usr/local/liferay/deploy
tar xvzfm portal.tar.gz -C /usr/local/liferay
The means of deployment is straighforward because only a few commands are needed for a deploy, and it is reproducable because the build server produces the build artifact. By effectively nuking the old binaries we are ensured that no files got left behind. This is a bigger problem than you might think. If you write a jsp hook then the jsp file you create will overwrite the one in the ROOT context. If in a later release of that hook you decide that you no longer whish to override that file you can take it out of the hook, but the original version of that file will not be restored when you deploy your new hook. To avoid surprises you should always start with a clean slate. There's downsides to this approach too though. If an administrator installed a new portlet via the control panel, then this portlet will be gone when a new version of the portal is deployed. Also it means we need different builds for different environments, because these environments all use slightly different settings, which is not ideal.
14. Do nightly deploys to a test server
Since we made deployment so easy, why not do it every night. You can configure your build server to deploy your portal to a remote server every night. That way the customer can monitor your progress. And you as a developer can verify that you indeed did check in all your files and that things work on more than just your machine. Ideally you would clear the database on every deploy. However in some cases timing constraints may warrant letting the customer already create some of the static pages on this nightly build environment while you develop your themes and plugins.
In this article I have listed a number of development practices that have worked out well for me. I am aware that some of the practices I just presented as best practices are actually considered anti patterns in much of the available literature but I believe in this case breaking the rules trumps the alternatives. However I am very much open to suggestions. Like I said nothing much (that i am aware of) has been written on the subject and I am very keen on hearing about how other people tackled this problem.
If you'd like to see how I organize a liferay SDK project you can check out a project template that i created on github. It was originally developed for liferay 6.0.x and was only recently upgraded to liferay 6.1 so if you use it and run into problems send me a message on github