Menu

Liferay commit transaction listener

Goal

To register and perform an action on a Liferay transaction commit successful

Description

The situation: in a given project, we had created a custom service builder entity named WorkRequest which has a state that controls if the work request should be shared with a 3rd party application or not (if the state is draft, it is simply maintained locally, on Liferay; on the other hand, if it is on state pending, it should be sent to a given external application – lets call it EXTERNAL APP 1 – and, finally, if it is on state pending_documents, it should be sent to another external application – lets call that one EXTERNAL APP 2).

To avoid issues regarding the communication with the external applications (e.g., the external application’s response time is too high or the external application is not online at a given time), we created an asynchronous integration mechanism, through Liferay message bus, trying to send almost all requests ASAP but not in a synchronous manner.

To do so, we have implemented the synchronisation with the external applications in two different situations:

ASAP (As soon as possible) – any time the user clicks on the submit button, the work request goes from the state draft to the state pending and at that moment, send a message to Liferay’s message bus so that the work request may be shared with the external application (this resulted in a problem, which may be solved with the current recipe)

SCHEDULER – a scheduler in the corresponding Liferay portlet that, from time to time, checks the work requests that are on state pending (to share with EXTERNAL APP 1) or on state pending_documents (to share with EXTERNAL APP 2) and processes them.

During the first situation, we ended up with funny things, such as:

The associated files were not shared with the external application

We would send more than once the work request to the external application

And we also realized that if we were in debug (actually, if we would include a small delay on the service method), the previous issues were not detected. So, it seemed to be a problem related to the boundaries of the transaction and its visibility. It seemed that, when the asynchronous listener was invoked, the transaction had not been finished yet and so, some of the data associated to the work request was still not available on the transaction that would be spawn on the message bus listener class. After some searches, we found out this Liferay thread which, although is not the exact same situation, is similar and explains a few things (including the trick that will make this recipe interesting).

How to

During this how to, we will include more than the solution in itself so that is may be easier for you to try out the same scenario. So, first, lets start with the class that registers the message destinations and the corresponding message listeners:

Now, we will show the service class where we have implemented the invocation to the External App 1 (ExternalApp1Client is a utility class that transforms our classes in the corresponding data and classes defined in the WSDL file of the external application client 1). Notice, however, that we do not show the method for the 2nd external application, which is similar to this one, with the exception that it does not need to send an asynchronous request because after the successful integration with the external application 2, there is no need to share anything else externally: