Overriding JSPs from multiple hooks - promising the cure

One message that I have to give out in trainings doesn't fail to stun the students: When you deploy multiple hooks that override the same JSP in Liferay, you will first get undefined behavior and later end up with a damaged installation.
CC BY 2.0 by David Goehring
When you are installing applications from Liferay's marketplace you don't even have control over the JSPs that these apps change. And there seem to be certain hotspots that everybody wants to change - experiencing a conflict is merely a question of time.

Out of the box you'll have to deal with this behavior on an organizational level - there's no safeguard, no tool, no oversight - not even logging - in Liferay. Yes, I admit: this is a shortcoming of the platform. And it puzzled me so often that I finally took action to solve this problem. After all, you can change any behavior of Liferay, right?

What's the problem?

Let's say you want to override /test/view.jsp. When you deploy the hook, Liferay will rename the original file to /test/view.portal.jsp and use your patched one as the new /test/view.jsp. Great - now you can include the original file from yours and add your changes (if you want). Undeploy of the hook reverses the process. Smart, right?

One problem now is that this is a one-step process:
CC BY 2.0 by David Goehring and yours truly
If you deploy a second hook overriding the same file, the mentioned procedure will repeat. In that process the original /test/view.jsp (later /test/view.portal.jsp) will be overwritten and lost forever. Now undeploy both hooks and no  /test/view.jsp is left. Did I tell you that deployment order of hooks is not guaranteed, so you never know what you end up with?

And the solution?

Due to the nature of hooks (deployment order not guaranteed), the solution must tie deeper into Liferay and an ext-plugin is in order: We need code that is running before the first hook is deployed. This will replace your original hook deployer with a safeguarding version - first testing for conflicts and denying deployment if there is any duplicate overridden JSP. Granted, it will not magically make everything work, but it will keep you from destroying your installation.

Check out the code on github - fork and send pullrequests: Beware, it's currently meant as a proof of concept. Please test, try & pound on it out on your platform & version. I have built and tested on 6.2 EE SP8, but due to the nature of the code it is probably working well on other versions and on CE with little or no change at all.

Due to being an ext-plugin this can't be made available on marketplace, but we can try it out here (and in future articles if necessary).

About the code

I've started with patching HookHotDeploymentListener.java - but when you look at that class you'll see that it's not meant to be extended. For all future upgrades this would have resulted in three-way-merges. And I hate three-way-merges. Thus I've changed the implementation to a wrapper. The new CheckingHookHotDeploymentListener just does its sanity check before delegating execution to the original HookHotDeploymentListener.

If you have your own ext-plugin already, you might want to move it in there, because I also need to override portal-ext.properties in order to replace the original hotdeploylistener: Each file (like portal-ext.properties) can be overridden from only one ext-plugin and the odds are that you want to change the same file anyway - portal-ext.properties is among the very popular hotspots for ext-changes.

Please give feedback and help testing it on other versions. There might be a distributable result if you help. And based on the twitter feedback (and the stunned faces in training) there seems to be a demand for this functionality.

I already have a portlet that shows the list of rejected contexts to the control panel and will share it soon . Find it on github as well. It will add itself to the Apps section of Control Panel. Until then you'll have to stick with the log output. For testing just create a few hooks that modify a new, currently unknown jsp and deploy them all - see if you can break it.

Update: Oh, look at this

Blogs
installing Liferay patches might overwrite hooked jsp's also
I believe that the patching tool warns when it finds hooked jsps. However, now that 6.2 EE SP12 is out, you might want to set the new option hot.deploy.hook.custom.jsp.verification.enabled=true in portal-ext.properties and get the same effect. Continue to use this ext if you want a more informative error message (e.g. which JSP was already deployed in which hook) or if you're on an older version or CE.
[...] As drastic UI changes happened in Liferay, these implementation need to be implemented on the new infrastructure, leveraging new techniquest (e.g. Lexicon). Also, most of the functionality has been... [...] Read More
Thanks Olaf for the blog!
We experienced this exact behaviour today when updating to the latest audience targeting (which did an override on a blog-JSP that we had previously hooked)
hello every one am working on hook in liferay 7 but problemn is am trying to overide the organization field and put my custom fields on organization,but thing is "Host OSGI bundle "their am not getting organization jar and override files,can pls send me solution..
As this question is in no way related to the blog article (and does not contain enough information on what you'd like to do): Please ask it on the forums: https://web.liferay.com/community/forums