Liferay Model Listeners

Liferay Model Listeners

How to create a custom model listener in Liferay


Overview


Model Listener is a Liferay extension point to inject custom functionality during persistence events (create, update, delete, or update association) on Liferay models. Developers can implement methods to be invoked before or after such an event occurs.


How does it work?


Model Listeners implement the ModelListener interface, which provides the following methods for listening to model update events:

  • onAfterAddAssociation(Object classPK, String associationClassName, Object associationClassPK)-executed after an association between two tables (a new entry in mapping table) has been added 

  • onAfterCreate(T model) - executed after a new model has been created

  • onAfterRemove(T model) - executed after a model has been removed

  • onAfterRemoveAssociation(Object classPK, String associationClassName, Object associationClassPK)- executed after an association between two tables (an entry in mapping table) has been deleted

  • onAfterUpdate(T originalModel, T model)- executed after a model has been updated

  • onBeforeAddAssociation(Object classPK, String associationClassName, Object associationClassPK)- executed before an association between two tables (a new entry in mapping table) has been added 

  • onBeforeCreate(T model)- executed before a new model has been created

  • onBeforeRemove(T model)-executed before a model has been removed

  • onBeforeRemoveAssociation(Object classPK, String associationClassName, Object associationClassPK) - executed before an association between two tables (an entry in mapping table) has been deleted

BaseModelListener class contains default (empty) implementations for all the methods defined in a ModelListener interface. This way, developers can extend from BaseModelListener and implement only methods they need.

All the model listener components are registered with a ModelListenerRegistrationUtil class (using ServiceTrackerCustomizer) and are used in the BasePersistenceImpl one (which is a base persistence class for any Liferay entity). For example, remove method contains the following code:


for (ModelListener<T> modelListener : modelListeners) {

  modelListener.onBeforeRemove(model);

}

T removedModel = removeImpl(model);

for (ModelListener<T> modelListener : modelListeners) {

  modelListener.onAfterRemove(model);

}


First, onBeforeRemove method is invoked for each registered model listener. Then removeImpl is invoked to remove the entity. Finally, onAfterRemove method is invoked for each model listener. 

An example of a model listener in Liferay sources is a GroupModelListener class in blogs module, which removes all blog subscriptions within a site before it’s deleted.


How to implement a custom Model Listener?


To implement a custom Model Listener we need to create a module in the Liferay Workspace:

with a class for the model listener (LifeDevUserModelListener here).

This class should extend BaseModelListener (parametrized with model class), and be declared as an OSGi component (@Component annotation) for ModelListener service:


    @Component(service = ModelListener.class)

public class LifeDevUserModelListener extends BaseModelListener<User> {


}


Then we need to override the required methods to be triggered when specific events are fired. For example, we may inject custom login after user is updated in the following way:


@Override

public void onAfterUpdate(User originalUser, User user) throws ModelListenerException {

   try {

       _log.info(String.format("User '%s' has been updated (original user: '%s').",

               user.getFullName(), originalUser.getFullName()));

   } catch (Exception e) {

       throw new ModelListenerException(e);

   }

}


Once we deploy this module and modify user first name / last name in the Account Settings - we’ll see the following message in logs:

[LifeDevUserModelListener:17] User 'Liferay Dev' has been updated (original user: 'Test Test').


If we need to intercept event after user has been added to an organization - we may use  onAfterAddAssociation method like this:  

@Override

public void onAfterAddAssociation(Object classPK, String associationClassName, Object associationClassPK) throws ModelListenerException {

   try {

       if (Organization.class.getName().equals(associationClassName)) {

           Long userId = (Long) classPK;

           Long organizationId = (Long) associationClassPK;

           Organization organization = organizationLocalService.fetchOrganization(organizationId);

           User user = userLocalService.fetchUser(userId);

           _log.info(String.format("Used #%d '%s' has been added to Organization #%d '%s'.",

                   userId, user.getFullName(), organizationId, organization.getName()));

       }

   } catch (Exception e) {

       throw new ModelListenerException(e);

   }

}


classPK here is the current model’s ID (userId in our case). associationClassName/associationClassPK are the class name and ID of the associated model. We may filter out only required associations using the className check, like in this example.

Once we redeploy the module and add a user to an organization - we’ll see in logs:

[LifeDevUserModelListener:37] Used #20124 'Liferay Dev' has been added to Organization #42309 'LifeDev'.


  In a similar way we can inject custom logic for any persistence event triggered for any entity in Liferay.


Enjoy 😏


Comments

Popular posts from this blog

Liferay Search Container Example

Liferay DXP - max upload file size

Liferay Keycloak integration