5.4.3.1. Extending notification system

eXo Platform provides you with a notification system that allows you to extend in 2 mechanisms:

This section will walk you through a complete sample extension that instructs you how to:

First you need to create a new Maven project with the overall structure:

And now, continue with the detailed steps:

Under pom.xml

Add the following dependencies to the pom.xml file:


<?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.acme.samples</groupId>
        <artifactId>console-notification</artifactId>
        <version>1.0.0</version>
        <packaging>pom</packaging>
        <modules>
            <module>lib</module>
            <module>config</module>
            <module>webapp</module>
        </modules>
        <properties>
            <org.exoplatform.depmgt.version>10-SNAPSHOT</org.exoplatform.depmgt.version>
            <org.exoplatform.kernel.version>2.4.9-GA</org.exoplatform.kernel.version>
            <org.exoplatform.core.version>2.5.9-GA</org.exoplatform.core.version>
            <!--GateIn project's dependencies-->
            <org.gatein.portal.version>3.5.10.Final</org.gatein.portal.version>
            <!--Platform project's dependencies-->
            <org.exoplatform.social.version>4.2.x-SNAPSHOT</org.exoplatform.social.version>
        </properties>
        <dependencyManagement>
            <dependencies>
                <!-- Import versions from platform project -->
                <dependency>
                    <groupId>org.exoplatform</groupId>
                    <artifactId>maven-depmgt-pom</artifactId>
                    <version>${org.exoplatform.depmgt.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <dependency>
                    <groupId>org.exoplatform.social</groupId>
                    <artifactId>social</artifactId>
                    <version>${org.exoplatform.social.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <!-- To be replaced by an import of GateIn Portal parent POM -->
                <dependency>
                    <groupId>org.gatein.portal</groupId>
                    <artifactId>exo.portal.component.portal</artifactId>
                    <version>${org.gatein.portal.version}</version>
                </dependency>
            </dependencies>
        </dependencyManagement>
    </project>

Under config folder

  1. Create a pom.xml file and two configuration.xml files under config folder as below:

  2. Add the following information to config/pom.xml:

    
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>com.acme.samples</groupId>
            <artifactId>console-notification</artifactId>
            <version>1.0.0</version>
        </parent>
        <artifactId>console-notification-config</artifactId>
        <packaging>jar</packaging>
        <build>
            <finalName>console-notification-config</finalName>
        </build>
    </project>
  3. Add the below configuration to conf/configuration.xml:

    
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd http://www.exoplatform.org/xml/ns/kernel_1_2.xsd"
    xmlns="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd">
        <external-component-plugins>
            <!-- The full qualified name of the PortalContainerConfig -->
            <target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
            <component-plugin>
                <!-- The name of the plugin -->
                <name>Add PortalContainer Definitions</name>
                <!-- The name of the method to call on the PortalContainerConfig in order to register the PortalContainerDefinitions -->
                <set-method>registerChangePlugin</set-method>
                <!-- The full qualified name of the PortalContainerDefinitionPlugin -->
                <type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
                    <priority>102</priority>
                    <init-params>
                        <values-param>
                            <name>apply.specific</name>
                            <value>portal</value>
                        </values-param>
                        <object-param>
                            <name>addDependencies</name>
                            <object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependencies">
                                <!-- The name of the portal container -->
                                <field name="dependencies">
                                    <collection type="java.util.ArrayList">
                                        <value>
                                            <!--The context name of the portal extension-->
                                            <string>console-notification-webapp</string>
                                        </value>
                                    </collection>
                                </field>
                            </object>
                        </object-param>
                    </init-params>
                </component-plugin>
        </external-component-plugins>
    </configuration>
  4. Add the following configuration to portal/configuration.xml:

    
    <?xml version="1.0" encoding="UTF-8"?>
        <configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd http://www.exoplatform.org/xml/ns/kernel_1_2.xsd"
        xmlns="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd">
            
            <external-component-plugins>
                <target-component>org.exoplatform.social.core.manager.IdentityManager</target-component>
    (1)            <component-plugin>
                    <name>SocialProfileListener</name>
                        <set-method>registerProfileListener</set-method>
                        <type>com.acme.samples.notification.SocialProfileListener</type>
                </component-plugin>
            </external-component-plugins>
            
            <external-component-plugins>
                <target-component>org.exoplatform.commons.api.notification.channel.ChannelManager</target-component>
    (2)            <component-plugin profiles="all">
                    <name>console.channel</name>
                        <set-method>register</set-method>
                        <type>com.acme.samples.notification.ConsoleChannel</type>
                        <description>Register the console channel to manager.</description>
                </component-plugin>
            </external-component-plugins>
            
            <external-component-plugins>
                <target-component>org.exoplatform.commons.api.notification.service.setting.PluginContainer</target-component>
                <component-plugin>
                        <name>notification.plugins</name>
    (3)                    <set-method>addPlugin</set-method>
                    <type>com.acme.samples.notification.plugin.UpdateProfilePlugin</type>
                        <description>Initial information for plugin.</description>
                        <init-params>
                            <object-param>
                                <name>template.UpdateProfilePlugin</name>
                                <description>The template of UpdateProfilePlugin</description>
                                <object
                                type="org.exoplatform.commons.api.notification.plugin.config.PluginConfig">
                                    <field name="pluginId">
                                        <string>UpdateProfilePlugin</string>
                                    </field>
                                    <field name="resourceBundleKey">
                                        <string>UINotification.label.UpdateProfilePlugin</string>
                                    </field>
                                    <field name="order">
                                        <string>11</string>
                                    </field>
                                    <field name="defaultConfig">
                                        <collection type="java.util.ArrayList">
                                            <value>
                                                <string>Instantly</string>
                                            </value>
                                        </collection>
                                    </field>
                                    <field name="groupId">
                                        <string>general</string>
                                    </field>
                                    <field name="bundlePath">
                                        <string>locale.notification.template.Notification</string>
                                    </field>
                                </object>
                            </object-param>
                        </init-params>
                </component-plugin>
            </external-component-plugins>
        </configuration>

    1

    Register SocialProfileListener as a profile listener plugin to the IdentityManager component. This plugin listens to user profile updating events.

    2

    Register new plugin console.channel to the ChannelManager component. This plugin pushes notifications to console panel.

    3

    Register new plugin UpdateProfilePlugin to the PluginContainer component. This plugin declares and initializes parameters for the new notification type. The initial parameters include:

    • template.UpdateProfilePlugin - the template of UpdateProfilePlugin.

    • pluginId - the Id of plugin which was defined in the class UpdateProfilePlugin.

    • resourceBundleKey - the key which will be provided in resource bundle files of each locale.

    • order - the order to display the new type in notification group.

    • groupId - the Id of group that this notification type belongs to.

    • bundlePath - the path to the locale resource.

    • defaultConfig - the default settings for this notification type at first startup.

Under lib folder

  1. Create another project under lib folder with the pom.xml file as below:

    
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>com.acme.samples</groupId>
            <artifactId>console-notification</artifactId>
            <version>1.0.0</version>
        </parent>
        <artifactId>console-notification-lib</artifactId>
        <packaging>jar</packaging>
        <dependencies>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.exoplatform.social</groupId>
                <artifactId>social-component-core</artifactId>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.exoplatform.core</groupId>
                <artifactId>exo.core.component.organization.api</artifactId>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.exoplatform.social</groupId>
                <artifactId>social-component-common</artifactId>
                <scope>provided</scope>
            </dependency>
        </dependencies>
        <build>
            <finalName>console-notification-lib</finalName>
        </build>
    </project>
  2. Implement the class UpdateProfilePlugin.java as follows:

    package com.acme.samples.notification.plugin;
    
    
        import java.util.ArrayList;
        import java.util.HashSet;
        import java.util.Set;
        import org.exoplatform.commons.api.notification.NotificationContext;
        import org.exoplatform.commons.api.notification.model.ArgumentLiteral;
        import org.exoplatform.commons.api.notification.model.NotificationInfo;
        import org.exoplatform.commons.api.notification.plugin.BaseNotificationPlugin;
        import org.exoplatform.commons.utils.CommonsUtils;
        import org.exoplatform.commons.utils.ListAccess;
        import org.exoplatform.container.xml.InitParams;
        import org.exoplatform.services.log.ExoLogger;
        import org.exoplatform.services.log.Log;
        import org.exoplatform.social.core.identity.model.Identity;
        import org.exoplatform.social.core.identity.model.Profile;
        import org.exoplatform.social.core.manager.RelationshipManager; (1)
        public class UpdateProfilePlugin extends BaseNotificationPlugin {
            public final static ArgumentLiteral<Profile> PROFILE = new ArgumentLiteral<Profile>(Profile.class, "profile");
            private static final Log LOG = ExoLogger.getLogger(UpdateProfilePlugin.class);
            public final static String ID = "UpdateProfilePlugin";
            
            public UpdateProfilePlugin(InitParams initParams) {
                super(initParams);
            }
            
            @Override
            public String getId() {
                return ID;
            }
            
            @Override
            public boolean isValid(NotificationContext ctx) {
                return true;
            }
            
            @Override (2)
            protected NotificationInfo makeNotification(NotificationContext ctx) {
                Profile profile = ctx.value(PROFILE);
                Set<String> receivers = new HashSet<String>();
                
                RelationshipManager relationshipManager = CommonsUtils.getService(RelationshipManager.class);
                Identity updatedIdentity = profile.getIdentity();
                ListAccess<Identity> listAccess = relationshipManager.getConnections(updatedIdentity);
                try {
                    Identity[] relationships =  relationshipManager.getConnections(updatedIdentity).load(0, listAccess.getSize());
                    for(Identity i : relationships) {
                        receivers.add(i.getRemoteId());
                    }
                    } catch (Exception ex) {
                    LOG.error(ex.getMessage(), ex);
                }
                
                return NotificationInfo.instance()
                .setFrom(updatedIdentity.getRemoteId())
                .to(new ArrayList<String>(receivers))
                .setTitle(updatedIdentity.getProfile().getFullName() + " updated his/her profile.<br/>")
                .key(getId());
            }
            
        }

    1

    This class extends BaseNotificationPlugin that retrieves information for new notification type of user profile updating event.

    2

    The makeNotification() method was overriden to generate essential information for a notification.

  3. Implement the class SocialProfileListener.java as below:

    package com.acme.samples.notification;
    
    
        import org.exoplatform.commons.api.notification.NotificationContext;
        import org.exoplatform.commons.api.notification.model.PluginKey;
        import org.exoplatform.commons.notification.impl.NotificationContextImpl;
        import org.exoplatform.social.core.identity.model.Profile;
        import org.exoplatform.social.core.profile.ProfileLifeCycleEvent;
        import org.exoplatform.social.core.profile.ProfileListenerPlugin;
        import com.acme.samples.notification.plugin.UpdateProfilePlugin; (1)
        public class SocialProfileListener extends ProfileListenerPlugin {
            
            @Override
            public void avatarUpdated(ProfileLifeCycleEvent event) {
                Profile profile = event.getProfile();
                NotificationContext ctx = NotificationContextImpl.cloneInstance().append(UpdateProfilePlugin.PROFILE, profile);
                ctx.getNotificationExecutor().with(ctx.makeCommand(PluginKey.key(UpdateProfilePlugin.ID))).execute(ctx);
            }
            
            @Override
            public void experienceSectionUpdated(ProfileLifeCycleEvent event) {
                Profile profile = event.getProfile();
                NotificationContext ctx = NotificationContextImpl.cloneInstance().append(UpdateProfilePlugin.PROFILE, profile);
                ctx.getNotificationExecutor().with(ctx.makeCommand(PluginKey.key(UpdateProfilePlugin.ID))).execute(ctx);
            }
            
            @Override
            public void contactSectionUpdated(ProfileLifeCycleEvent event) {
                Profile profile = event.getProfile();
                NotificationContext ctx = NotificationContextImpl.cloneInstance().append(UpdateProfilePlugin.PROFILE, profile);
                ctx.getNotificationExecutor().with(ctx.makeCommand(PluginKey.key(UpdateProfilePlugin.ID))).execute(ctx);
            }
            
            @Override
            public void createProfile(ProfileLifeCycleEvent event) {
                Profile profile = event.getProfile();
                NotificationContext ctx = NotificationContextImpl.cloneInstance().append(UpdateProfilePlugin.PROFILE, profile);
                ctx.getNotificationExecutor().with(ctx.makeCommand(PluginKey.key(UpdateProfilePlugin.ID))).execute(ctx);
            }
            
        }

    1

    This class extends ProfileListenerPlugin to trigger user profile updating events and plug them into UpdateProfilePlugin as notifications. The instance of UpdateProfilePlugin will be used to generate and send messages to all notification channels.

    • avatarUpdated() - trigger avatar updating event.

    • experienceSectionUpdated() - trigger user experience updating event.

    • contactSectionUpdated() - trigger user contact updating event.

    • createProfile() - trigger user profile creating event.

  4. Implement the class ConsoleChannel.java to have the following code:

    package com.acme.samples.notification;
    
    
        import java.io.Writer;
        import org.exoplatform.commons.api.notification.NotificationContext;
        import org.exoplatform.commons.api.notification.channel.AbstractChannel;
        import org.exoplatform.commons.api.notification.channel.template.AbstractTemplateBuilder;
        import org.exoplatform.commons.api.notification.channel.template.TemplateProvider;
        import org.exoplatform.commons.api.notification.model.ChannelKey;
        import org.exoplatform.commons.api.notification.model.MessageInfo;
        import org.exoplatform.commons.api.notification.model.NotificationInfo;
        import org.exoplatform.commons.api.notification.model.PluginKey;
        import org.exoplatform.commons.notification.lifecycle.SimpleLifecycle;
        import org.exoplatform.services.log.ExoLogger;
        import org.exoplatform.services.log.Log; (1)
        public class ConsoleChannel extends AbstractChannel {
            
            private static final Log LOG = ExoLogger.getLogger(ConsoleChannel.class);
            private final static String ID = "CONSOLE_CHANNEL";
            private final ChannelKey key = ChannelKey.key(ID);
            
            public ConsoleChannel() {
                super(new SimpleLifecycle());
            }
            
            @Override
            public String getId() {
                return ID;
            }
            
            @Override
            public ChannelKey getKey() {
                return key;
            }
            
            @Override (2)
            public void dispatch(NotificationContext ctx, String userId) {
                LOG.info(String.format("CONSOLE:: %s will receive the message from pluginId: %s",
                userId,
                ctx.getNotificationInfo().getKey().getId()));
            }
            
            @Override
            public void registerTemplateProvider(TemplateProvider provider) {}
            
            @Override
            protected AbstractTemplateBuilder getTemplateBuilderInChannel(PluginKey key) {
                return new AbstractTemplateBuilder() {
                    @Override
                    protected MessageInfo makeMessage(NotificationContext ctx) {
                        return null;
                    }
                    @Override
                    protected boolean makeDigest(NotificationContext ctx, Writer writer) {
                        return false;
                    }
                };
            }
        }

    1

    This concrete class extends AbstractChannel to define a new notification channel which sends messages to console panel. Any new channel must implement this interface and use an external-component-plugin configuration to be registered in the ChannelManager.

    2

    The dispatch() method was overriden to write notification contents to console panel.

Under webapp folder

  1. Create a new Maven project inside webapp folder with the following pom.xml file:

    
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>com.acme.samples</groupId>
            <artifactId>console-notification</artifactId>
            <version>1.0.0</version>
        </parent>
        <artifactId>console-notification-webapp</artifactId>
        <packaging>war</packaging>
        <build>
            <finalName>console-notification-webapp</finalName>
        </build>
    </project>
  2. Add the following configurations to WEB-INF/web.xml:

    
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="3.0"
    metadata-complete="true"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
        <display-name>console-notification-webapp</display-name>
        <filter>
            <filter-name>ResourceRequestFilter</filter-name>
            <filter-class>org.exoplatform.portal.application.ResourceRequestFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>ResourceRequestFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    </web-app>
    • display-name - should be the same as the context name of the portal extension.

  3. Open Notification_en.properties file to add this text:

    ###########################################
    
    #                         UpdateProfilePlugin                         #
    ###########################################
    For UI
    UINotification.title.UpdateProfilePlugin= Someone updates profile
    UINotification.label.UpdateProfilePlugin= Someone updates profile

    This is a resource bundle for English language. The value of UINotification.title.UpdateProfilePlugin and UINotification.label.UpdateProfilePlugin will be used to display as English name of the new notification type through user interface.

Testing

  1. Go up to the parent project's folder and build it with the command: mvn clean install.

  2. Copy the generated jar and war files into the corresponding deployment folders where you unpacked the eXo Platform installation.

  3. Start eXo Platform and you will see your new functions appear in Notification Settings:

  4. Log in as a user and update avatar or experience (remember to enable notification plugins first by an administrator).

    Now, a message informing about this activity will be pushed to all notification channels, for instance:

    • directly on-site:

    • or on the console, there will be a message for each user who is connecting with the above user, such as:

      James will receive the message from pluginId: UpdateProfilePlugin
Copyright ©. All rights reserved. eXo Platform SAS
blog comments powered byDisqus