Liferay Custom Notifications

Java tutorial on how to handle Liferay custom notifications. There is very little good information on this topic but I’ve seen a good need for a proper documentation so here’s a nice example of how to handle Liferay User Notifications based on Liferay 6.2 Notifications API. The official documentation on Liferay notifications is rather weak but then again the feature is also rather new and it’s not even 100% part of the core Liferay experience. Liferay 6.2 notifications have improved quite well and I feel it’s time to make them a first-class feature.

If you download the Liferay 6.2 bundle with Tomcat then you have the Notifications portlet installed already. If you installed Liferay on your own Tomcat or other Application Server using the WAR installation method then you need to install Notifications Portlet from the Marketplace.
Here’s a link to the Notifications CE Portlet on Liferay Marketplace.

Liferay custom notifications require a User Notification Handler class that turns the notification into a nice HTML fragment that is displayed to the user. Also Liferay wants us to define the notification types that our portlet or application is creating in a definition file upfront.

So the first thing we need to do is to define the location of our class that extends BaseUserNotificationHandler class and definitions inside liferay-portlet.xml. This can be done in liferay-portlet.xml like this:


<portlet>
    <portlet-name>example</portlet-name>
    <icon>/icon.png</icon>
    <user-notification-definitions>example-user-notification-definitions.xml</user-notification-definitions>
    <user-notification-handler-class>com.example.notifications.ExampleUserNotificationHandler</user-notification-handler-class>		
</portlet>

The important part is user-notification-definitions and user-notification-handler-class tags. Now we need to actually define our notification inside that example-user-notification-definitions.xml. This file should go into your resources folder. Since I’m using a maven based project then this file goes into src/main/resources/:

location of example-user-notification-definitions.xml

If you are running an Ant based project then you have to place this file inside docroot/WEB-INF/src/ folder.

Here’s the example-user-notification-definitions.xml I created:


<?xml version="1.0"?>
<!DOCTYPE user-notification-definitions PUBLIC "-//Liferay//DTD User Notification Definitions 6.2.0//EN" "http://www.liferay.com/dtd/liferay-user-notification-definitions_6_2_0.dtd">
<user-notification-definitions>
	<definition>
		<notification-type>${com.example.notifications.ExampleUserNotificationHandler.PORTLET_ID}</notification-type>
		<description>receive-a-notification-when-example-triggered</description>
		<delivery-type>
			<name>email</name>
			<type>${com.liferay.portal.model.UserNotificationDeliveryConstants.TYPE_EMAIL}</type>
			<default>false</default>
			<modifiable>true</modifiable>
		</delivery-type>
		<delivery-type>
			<name>website</name>
			<type>${com.liferay.portal.model.UserNotificationDeliveryConstants.TYPE_WEBSITE}</type>
			<default>true</default>
			<modifiable>true</modifiable>
		</delivery-type>
	</definition>
</user-notification-definitions>

Notice how we are using some public final Strings from class files like this: ${com.example.notifications.ExampleUserNotificationHandler.PORTLET_ID}. This allows us to define our Portlet ID in one location and keep things a little bit simpler.
Ok, so now we have our example defined and we can actually Add a new notification for the user. We can do so by using the built-in UserNotificationEventLocalServiceUtil in either our Service Builder class or one of our portlet classes.

Here’s how to add a new notification event:

    JSONObject payloadJSON = JSONFactoryUtil.createJSONObject();
    payloadJSON.put("userId", user.getUserId());
    payloadJSON.put("yourCustomEntityId", exampleEntity.getEntityId());
    payloadJSON.put("additionalData", "Your notification was added!");

    UserNotificationEventLocalServiceUtil.addUserNotificationEvent(user.getUserId(), 
		com.example.notifications.ExampleUserNotificationHandler.PORTLET_ID, 
		(new Date()).getTime(),
		user.getUserId(),
		payloadJSON.toString(),
		false, serviceContext);		

If you are doing this inside a portlet then you need the serviceContext, you can get one like this:

ServiceContext serviceContext = ServiceContextFactory.getInstance(portletRequest);

So now that we have our notification inside the database we want to actually show it to the user. When we look at the dockbar our notification number gets increased so we can be pretty sure that the notification was added. When you attempt to actually look at the notification you will probably see a blank white box. We need the notification handler class to actually handle the display side of our notification. This current example also handles the behavior like providing Approve and Reject buttons right in our example notification so that our example user could perform an action straight from the dockbar notification and not waste extra time when it’s a trivial task. I don’t have the correct screenshot but the look we are going after is similar to that you can see on this picture:
liferay custom notifications

Basic idea being we have an Approve and Reject buttons on our notification, so that you can click on the notification or it’s background to go and View more details, but you can also directly perform some custom actions like Approve or Reject in this Liferay custom notification tutorial. The buttons could also be “Report as Spam” or “Notify the Security”. It’s all up to you where that link takes the user.
So here’s our handler class:


import javax.portlet.ActionRequest;
import javax.portlet.PortletURL;
import javax.portlet.WindowState;

import com.liferay.portal.kernel.json.JSONFactoryUtil;
import com.liferay.portal.kernel.json.JSONObject;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.notifications.BaseUserNotificationHandler;
import com.liferay.portal.kernel.portlet.LiferayPortletResponse;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.model.UserNotificationEvent;
import com.liferay.portal.service.ServiceContext;
import com.liferay.portal.service.UserNotificationEventLocalServiceUtil;

public class ExampleUserNotificationHandler extends
		BaseUserNotificationHandler {
	
	public static final String PORTLET_ID = "example_WAR_exampleportlet";
	
	public ExampleUserNotificationHandler() {

		setPortletId(com.example.notifications.ExampleUserNotificationHandler.PORTLET_ID);

	}

	@Override
	protected String getBody(UserNotificationEvent userNotificationEvent,
			ServiceContext serviceContext) throws Exception {

		JSONObject jsonObject = JSONFactoryUtil
				.createJSONObject(userNotificationEvent.getPayload());

		long yourCustomEntityId = jsonObject
				.getLong("yourCustomEntityId");

		String title = "<strong>Example notification for entity ID "
				+ yourCustomEntityId
				+ "</strong>";

		String bodyText = "Some other text.";

		LiferayPortletResponse liferayPortletResponse = serviceContext
				.getLiferayPortletResponse();

		PortletURL confirmURL = liferayPortletResponse.createActionURL(com.example.notifications.ExampleUserNotificationHandler.PORTLET_ID);

		confirmURL.setParameter(ActionRequest.ACTION_NAME, "doSomethingGood");
		confirmURL.setParameter("redirect", serviceContext.getLayoutFullURL());
		confirmURL.setParameter("yourCustomEntityId", String.valueOf(yourCustomEntityId));
		confirmURL.setParameter("userNotificationEventId", String.valueOf(userNotificationEvent.getUserNotificationEventId()));
		confirmURL.setWindowState(WindowState.NORMAL);

		PortletURL ignoreURL = liferayPortletResponse.createActionURL(com.example.notifications.ExampleUserNotificationHandler.PORTLET_ID);
		ignoreURL.setParameter(ActionRequest.ACTION_NAME, "cancelForExample");
		ignoreURL.setParameter("redirect", serviceContext.getLayoutFullURL());
		ignoreURL.setParameter("yourCustomEntityId", String.valueOf(yourCustomEntityId));
		ignoreURL.setParameter("userNotificationEventId", String.valueOf(userNotificationEvent.getUserNotificationEventId()));
		ignoreURL.setWindowState(WindowState.NORMAL);

		String body = StringUtil.replace(getBodyTemplate(), new String[] {
				"[$CONFIRM$]", "[$CONFIRM_URL$]", "[$IGNORE$]",
				"[$IGNORE_URL$]", "[$TITLE$]", "[$BODY_TEXT$]" }, new String[] {
				serviceContext.translate("approve"), confirmURL.toString(),
				serviceContext.translate("reject"), ignoreURL.toString(),
				title, bodyText });
		
		return body;
	}

	@Override
	protected String getLink(UserNotificationEvent userNotificationEvent,
			ServiceContext serviceContext) throws Exception {

		JSONObject jsonObject = JSONFactoryUtil
				.createJSONObject(userNotificationEvent.getPayload());

		long yourCustomEntityId = jsonObject
				.getLong("yourCustomEntityId");
		
		LiferayPortletResponse liferayPortletResponse = serviceContext
				.getLiferayPortletResponse();		

		PortletURL viewURL = liferayPortletResponse.createActionURL(com.example.notifications.ExampleUserNotificationHandler.PORTLET_ID);
		viewURL.setParameter(ActionRequest.ACTION_NAME, "showDetails");
		viewURL.setParameter("redirect", serviceContext.getLayoutFullURL());
		viewURL.setParameter("yourCustomEntityId", String.valueOf(yourCustomEntityId));
		viewURL.setParameter("userNotificationEventId", String.valueOf(userNotificationEvent.getUserNotificationEventId()));
		viewURL.setWindowState(WindowState.NORMAL);
		
		return viewURL.toString();
	}

	protected String getBodyTemplate() throws Exception {
		StringBundler sb = new StringBundler(5);
		sb.append("<div class=\"title\">[$TITLE$]</div><div ");
		sb.append("class=\"body\">[$BODY_TEXT$]<a class=\"btn btn-action ");
		sb.append("btn-success\" href=\"[$CONFIRM_URL$]\">[$CONFIRM$]</a>");
		sb.append("<a class=\"btn btn-action btn-warning\" href=\"");
		sb.append("[$IGNORE_URL$]\">[$IGNORE$]</a></div>");
		return sb.toString();
	}

}

NB Important Information: The com.example.notifications.ExampleUserNotificationHandler.PORTLET_ID string that you use as your notification type has to match an actual portlet ID. It doesn’t actually need to be YOUR portlet ID but that would be the right thing to have there. The reason being that Notifications display portlet uses it to display a small portlet icon next to your notification to help the user identify the source of the notification. Providing a bad Portlet ID or something like null leads to a hard-to-trace NullPointerException in the JSP. Took me an hour to track it down.

The problematic part is this code from notifications-portlet view_entries.jsp source

<span class="portlet-icon">
<liferay-portlet:icon-portlet portlet="<%= PortletLocalServiceUtil.getPortletById(company.getCompanyId(), userNotificationEvent.getType()) %>" />
</span>

If you have a question or having trouble getting it working then feel free to write me a comment and I’ll do my best to help you out. If you would like to read more about Liferay related subjects then also make sure to comment on that and if you liked what you read then make sure to subscribe – It’s FREE!

About Reigo Reinmets

Enterprise Software consultant with 9 years of experience in enterprise software world who’s working on various projects that are mostly based on Liferay. Most blog posts here are real-life findings and issues we’ve encountered while working on projects and Java tutorials on Liferay.

107 thoughts on “Liferay Custom Notifications

  1. Hi! i like your post very much!!, but i have an issue…
    the class you created is named:
    ExamplementUserNotificationHandler
    is that a typo from the class in liferay-portlet.xml? (ExampleUserNotificationHandler) or what does the class ExampleUserNotificationHandler have?
    also, that is a spring portlet i guess? can you upload the code? 🙂

    thank you!! 🙂

    1. Thanks for the heads-up. I Quickly renamed the code when I made this tutorial so that was a typo. Corrected by now.
      No, that is not a Spring portlet. It’s a typical UserNotificationHandler class that extends com.liferay.portal.kernel.notifications.BaseUserNotificationHandler. The rest of the portlet is basically a random liferay com.liferay.util.bridges.mvc.MVCPortlet that “should” handle action requests like “showDetails”, “doSomethingGood” and “cancelForExample”.

      Something like this in your portlet class should do:

      	public void doSomethingGood(ActionRequest request, ActionResponse response)
      			throws IOException, PortletException {
      
      		ThemeDisplay themeDisplay = (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);
      		long groupId = themeDisplay.getScopeGroupId();
      		long yourCustomEntityId = ParamUtil.getLong(request, "yourCustomEntityId");
      		long userNotificationEventId = ParamUtil.getLong(request, "userNotificationEventId");
      		// Do the good thing you wanted to do...
      		
      		try {
      			
      			// Perhaps delete the notification now that the user has reacted? 
      			UserNotificationEventLocalServiceUtil.deleteUserNotificationEvent(userNotificationEventId);
      			
      		} catch (PortalException e) {
      			LOG.debug("Failed to remove notification:"+e.getMessage(), e);
      		} catch (SystemException e) {
      			LOG.debug("Failed to remove notification:"+e.getMessage(), e);
      		}
      
      	}
      
      
  2. i am using your code but when i am running this it giving me error like

    “liferay unable to open resource in class loader example-user-notification-definitions.xml”

    please help me in this…

    thanx in advance

    1. Hi, most likely your notification definition XML file is not found – it’s in a wrong place.
      I updated the post to show that the files should go into src/main/resources/ folder if you are running a maven project.

      If you are running an Ant based project then put it inside docroot/WEB-INF/src/ folder.

  3. Hello,

    Thank you for this awesome post,

    However I have a question, a good idea to hardcode the portlet id ?

    Thx

    1. Hi

      Might not be the perfect solution, but you need to hard-code the portletID into portlet.xml anyway and your webapp name is also rather unlikely to change.
      Also, note that last part of the post:

      notification type has to match an actual portlet ID. The reason being that Notifications display portlet uses it to display a small portlet icon next to your notification to help the user identify the source of the notification. Providing an in-existing ID leads to a hard-to-trace NullPointerException in the JSP.

  4. Hi,

    First of all, thank you for this great article.

    I have a question:

    Can you please let me know how did you manage to get the serviceContext? I want to add a notification from my portlet and not the service builder.

    Thank you.

    1. Hi

      You can get serviceContext from a portlet request like this:

      ServiceContext serviceContext = ServiceContextFactory.getInstance(portletRequest);
      
  5. Great tutorial, but for email notification?
    I’m verifying that web notification are properly displayed, but email are not sent, even if mail server is configured in control pannel…

    1. I’m afraid it’s very difficult for me to diagnose your problem regarding the e-mail notification. That’s not part of this tutorial and our clients haven’t required e-mail notifications either so far.

      As far as I know the e-mail notifications are personalized and in my example they are not sent by default but user should be able to enable them in their notifications portlet. They are also probably only sent when the user has not seen the notification on the portal already.

  6. Hi! First of all, thanks for the tremendous effort you must have done to get this tutorial done. I have one question. If I place the liferay-portlet.xml with the data you showed, it says that it’s not a valid tag, Am I missing something? Maybe on portlet.xml?

    I followed everything and the notification counter gets increased but no information about the notification comes up.

    1. @Ernest: user-notification-definitions and user-notification-handler-class have to come right after icon tag. Your IDE is probably complaining since your XML tag’s are in the wrong order. Try moving them. If you still have problems then send me a mail with your liferay-portlet.xml and I’ll take a look.

  7. Hi! First of all, thanks for code of Notification in this site but i’ve a problem with Title of Notification Event
    I want to change charset encoding UTF-8 for display thai letter and change design of sender and etc.

    Can I custom display of Notification in my style

    Thx for reply

    1. Hi, I’m afraid I don’t really understand your problem. All the strings should already be in UTF-8 so you should not have any problems with Thai characters. If you still haven’t found a solution then can you please send me a screenshot or a specific example on where you have a problem with characters?

      1. Thx for Reply First

        Now I can show thai character already. now I want to redesign that notify I seen tag sender and another tag in that notify. Can I remove it or Change Image of sender to other image from JSON Payload

  8. Hi Reigo,

    really, a great article – very helpful; I have a question though – wonder if you happen to know anything. I’m wondering – what is the meaning and purpose of that “delivery type” that can be email, IM, etc. As I gather one can not specify delivery time on creating a new user notification event. So the question is – why those delivery types re needed at all?

    1. @AUdalykh:
      The delivery types define in what ways can Liferay send this notification to the user. The example I’ve given here are Website and E-Mail. This means that when the user logs into liferay then they will see the notification in their notifications portlet (and dockbar notifications). If the user has not logged in to liferay in X amount of time then the notifications should be sent to the E-mail.

      If you look at the notifications portlet you will see that the user is allowed to select what type of notifications they want on what types of delivery methods.

      There are actually many delivery methods:
      TYPE_EMAIL
      TYPE_IM
      PRIVATE_MESSAGE
      TYPE_PUSH
      TYPE_SMS
      TYPE_WEBSITE

      How each of these behave is a little bit different and may require the server administrator to configure the corresponding delivery types (i.e. sending SMS) and private message or IM require perhaps additional components installed on Liferay.
      (Note that TYPE_PUSH is not available in 6.2 yet, it’s available only in the upcoming Liferay 7).

      1. If the user has not logged in to liferay in X amount of time then the notifications should be sent to the E-mail.

        Do you know how to configure the X, i.e. the amount of time then the notifications should be sent as an E-mail?

      2. Hi Reigo,
        I’m develop website and now I can send notification from one user to another user

        but now I’ve to create a scheduler class in liferay and that class will call service and send notification to more user.
        Can i do this process? b’coz I can’t get ActionRequest to get contextService and some parameter to pass value to “UserNotificationEventLocalServiceUtil .addUserNotificationEvent”

      3. These delivery methods are defined in UserNotificationDeliveryConstants, but after having grepped through the entire codebase it doesn’t look like they are used anywhere.
        In fact UserNotificationDeliveryConstants doesn’t seem to be referenced anywhere.
        Which leaves me wondering if any of them other than TYPE_WEBSITE are actually implemented. I haven’t found any mention of a successful use of TYPE_EMAIL, for instance, which is what started my search in the first place.

      4. I typed too soon. If you look in the Private Messaging portlet in the plugins repository TYPE_EMAIL is used there. That code has it’s own method to send email. Given that this is Liferay’s own code that’s a pretty strong hint that anything other than TYPE_WEBSITE is strictly ‘roll your own’.

      5. Yes – You are correct. I think I mistakenly assumed that it’s actually implemented.

  9. @Gl2yfon:
    “I want to redesign that notify I seen tag sender and another tag in that notify. Can I remove it or Change Image of sender to other image from JSON Payload.”

    Unless you modify the notification display via hook you cannot remove it or modify it.
    Another option would be to hide it using a CSS in your theme.

    The “sender” is taken from the Payloads userId field, so you can define who’s the person being shown in the notification.
    https://github.com/liferay/liferay-plugins/blob/master/portlets/notifications-portlet/docroot/notifications/view_entry.jspf:

    JSONObject userNotificationEventJSONObject = JSONFactoryUtil.createJSONObject(userNotificationEvent.getPayload());
    long userId = userNotificationEventJSONObject.getLong("userId");
    String userFullName = HtmlUtil.escape(PortalUtil.getUserName(userId, StringPool.BLANK));
    String userPortaitURL = StringPool.BLANK;
    User curUser = UserLocalServiceUtil.fetchUserById(userId);
    if (curUser != null) {
    	userPortaitURL = curUser.getPortraitURL(themeDisplay);
    }
    

    There is a reason for that – it makes the users see who’s doing what and you should probably try to embrace it and make your notifications look like they originated from SOMEONE. This increases their value AND improves the social communication between users. It sort of makes the notifications more personal – my boss asked me to approve it, my co-worker sent me flowers, my undergrad finished evaluating some paper etc.

      1. Yes, that means that the userId you specify in the notification payload has to exist in your Liferay database. You CAN change that but you have to write a custom hook for it to over-write notification portlet’s view_entries and view_entry files.

        You can try sending “0” as the userId, it seems the good people at Liferay have tried to handle the case that the user is not found and display an empty image instead etc…

      2. Hi Reigo,

        Now I can add Notification and Fix body and title already.

        But I’ve new problem with new function.

        I’ve a java job run to clear something in db and it’s must to add notification to liferay too. Can I do It.
        I can’t find portletId to send and fix with xml to config that Notification

  10. Hi Reigo, thank you for this tutorial, it is the only resource for this topic I have found so far.
    I have a question – I have implemented your code and the notification I add is displayed only for the particular user that invoked the action. How do I add notification for all the site members, or how can I select the group of users for which will be the notification displayed? Thanks!

    Jan

  11. @Gl2yfon I hope this doesn’t come too late but I’ve been really busy and haven’t had time to go through the posts here.

    You might want to read this about ServiceContext: https://www.liferay.com/documentation/liferay-portal/6.2/development/-/ai/servicecontext-liferay-portal-6-2-dev-guide-06-en

    You can manually create the ServiceContext object for things if you really need it like this:

    ServiceContext serviceContext = new ServiceContext();
    serviceContext.setScopeGroupId(myGroupId);
    ...
    
  12. Great work, good wiki and very usefull.
    I was able to customize user notification but I’ve 2 question:
    – The user that has create the content in the notification is not showed (it has an empty circle and no name), we have to customize in some part of the code?
    – what about notify document (not only web content)?

    1. 1) If I remember correctly then Liferay uses the userId value from the Notification Payload for showing the user info.

      JSONObject payloadJSON = JSONFactoryUtil.createJSONObject();
      payloadJSON.put("userId", user.getUserId());
      

      2) I don’t understand the question but you can send what-ever notification you want and link it to what-ever page or portlet you want so you should be able to notify about documents etc also.

  13. Hi, very useful information but I am facing problem while displaying the notification.
    I have used the exact code which you have mentioned, the portlet is getting deployed without errors, number of notification is getting increased with each add event but no information is displayed. When I attempt to actually look at the notification I am getting blank white box.
    How will I be able to see the details of the notifications.
    Thanks

    1. In my example Liferay is using ExampleUserNotificationHandler to actually generate the content it should have in the notification. Method called getBody() is responsible of returning the HTML that should be the contents of your notification. If you are only getting a blank white box then please add some Debugging statements into getBody and make sure it’s being actually called out.

      If it’s being called out then check the output of that method before returning – it should be a valid HTML.

      Also try to debug the getLink method in that class.

      If it’s not being called at all then perhaps you have an issue with the definition – Is the package and class names correct?

      1. Thanks Reigo for the prompt reply.

        The methods are being called in the sequence getBody(), getBodyTemplate(), getLink().
        However i am getting NullPointerException in console now as :

        SEVERE: Servlet.service() for servlet jsp threw exception
        java.lang.NullPointerException
        at com.liferay.taglib.util.IncludeTag.cleanUpSetAttributes(IncludeTag.java:182)
        at com.liferay.taglib.util.IncludeTag.doEndTag(IncludeTag.java:95)
        at org.apache.jsp.notifications.view_005fentries_jsp._jspService(view_005fentries_jsp.java:559)
        at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
        at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
        at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
        at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:116)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilter.doFilter(InvokerFilter.java:96)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:749)
        at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:605)
        at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:544)
        at com.liferay.portlet.PortletRequestDispatcherImpl.dispatch(PortletRequestDispatcherImpl.java:331)
        at com.liferay.portlet.PortletRequestDispatcherImpl.include(PortletRequestDispatcherImpl.java:112)
        at com.liferay.util.bridges.mvc.MVCPortlet.include(MVCPortlet.java:371)
        at com.liferay.util.bridges.mvc.MVCPortlet.include(MVCPortlet.java:387)
        at com.liferay.util.bridges.mvc.MVCPortlet.doDispatch(MVCPortlet.java:320)
        at javax.portlet.GenericPortlet.render(GenericPortlet.java:233)
        at com.liferay.portlet.FilterChainImpl.doFilter(FilterChainImpl.java:103)
        at com.liferay.portlet.ScriptDataPortletFilter.doFilter(ScriptDataPortletFilter.java:55)
        at com.liferay.portlet.FilterChainImpl.doFilter(FilterChainImpl.java:100)
        at com.liferay.portal.kernel.portlet.PortletFilterUtil.doFilter(PortletFilterUtil.java:64)
        at com.liferay.portal.kernel.servlet.PortletServlet.service(PortletServlet.java:112)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:116)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilter.doFilter(InvokerFilter.java:96)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:749)
        at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:605)
        at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:544)
        at com.liferay.portlet.InvokerPortletImpl.invoke(InvokerPortletImpl.java:604)
        at com.liferay.portlet.InvokerPortletImpl.invokeRender(InvokerPortletImpl.java:677)
        at com.liferay.portlet.InvokerPortletImpl.render(InvokerPortletImpl.java:379)
        at org.apache.jsp.html.portal.render_005fportlet_jsp._jspService(render_005fportlet_jsp.java:1242)
        at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
        at com.liferay.portal.servlet.DirectRequestDispatcher.include(DirectRequestDispatcher.java:57)
        at com.liferay.portal.servlet.ClassLoaderRequestDispatcherWrapper.doDispatch(ClassLoaderRequestDispatcherWrapper.java:78)
        at com.liferay.portal.servlet.ClassLoaderRequestDispatcherWrapper.include(ClassLoaderRequestDispatcherWrapper.java:53)
        at com.liferay.portlet.PortletContainerImpl._doRender(PortletContainerImpl.java:655)
        at com.liferay.portlet.PortletContainerImpl.render(PortletContainerImpl.java:138)
        at com.liferay.portlet.SecurityPortletContainerWrapper.render(SecurityPortletContainerWrapper.java:141)
        at com.liferay.portlet.RestrictPortletContainerWrapper.render(RestrictPortletContainerWrapper.java:126)
        at com.liferay.portal.kernel.portlet.PortletContainerUtil.render(PortletContainerUtil.java:156)
        at com.liferay.portal.action.LayoutAction.processLayout(LayoutAction.java:373)
        at com.liferay.portal.action.LayoutAction.doExecute(LayoutAction.java:178)
        at com.liferay.portal.action.LayoutAction.execute(LayoutAction.java:79)
        at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431)
        at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236)
        at com.liferay.portal.struts.PortalRequestProcessor.process(PortalRequestProcessor.java:173)
        at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1196)
        at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
        at com.liferay.portal.servlet.MainServlet.callParentService(MainServlet.java:546)
        at com.liferay.portal.servlet.MainServlet.service(MainServlet.java:523)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:116)
        at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:169)
        at com.liferay.portal.servlet.filters.secure.SecureFilter.processFilter(SecureFilter.java:293)
        at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:59)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:204)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:109)
        at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:169)
        at com.liferay.portal.servlet.filters.jsoncontenttype.JSONContentTypeFilter.processFilter(JSONContentTypeFilter.java:42)
        at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:59)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:204)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:109)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilter.doFilter(InvokerFilter.java:96)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:749)
        at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:487)
        at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:412)
        at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:339)
        at com.liferay.portal.servlet.FriendlyURLServlet.service(FriendlyURLServlet.java:160)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:116)
        at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:169)
        at com.liferay.portal.servlet.filters.strip.StripFilter.processFilter(StripFilter.java:359)
        at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:59)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:204)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:109)
        at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:169)
        at com.liferay.portal.servlet.filters.gzip.GZipFilter.processFilter(GZipFilter.java:123)
        at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:59)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:204)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:109)
        at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:169)
        at com.liferay.portal.servlet.filters.secure.SecureFilter.processFilter(SecureFilter.java:293)
        at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:59)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:204)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:109)
        at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:169)
        at com.liferay.portal.servlet.filters.i18n.I18nFilter.processFilter(I18nFilter.java:243)
        at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:59)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:204)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:109)
        at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:169)
        at com.liferay.portal.servlet.filters.jsoncontenttype.JSONContentTypeFilter.processFilter(JSONContentTypeFilter.java:42)
        at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:59)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:204)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:109)
        at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:169)
        at com.liferay.portal.servlet.filters.autologin.AutoLoginFilter.processFilter(AutoLoginFilter.java:263)
        at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:59)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:204)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:109)
        at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:169)
        at com.liferay.portal.servlet.filters.sso.ntlm.NtlmPostFilter.processFilter(NtlmPostFilter.java:83)
        at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:59)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:204)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:109)
        at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:169)
        at com.liferay.portal.sharepoint.SharepointFilter.processFilter(SharepointFilter.java:88)
        at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:59)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:204)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:109)
        at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:169)
        at com.liferay.portal.servlet.filters.virtualhost.VirtualHostFilter.processFilter(VirtualHostFilter.java:226)
        at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:59)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:204)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:109)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDirectCallFilter(InvokerFilterChain.java:185)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:96)
        at org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:738)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:204)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:109)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDirectCallFilter(InvokerFilterChain.java:165)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:96)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDirectCallFilter(InvokerFilterChain.java:165)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:96)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDirectCallFilter(InvokerFilterChain.java:185)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:96)
        at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilter.doFilter(InvokerFilter.java:96)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.lang.Thread.run(Unknown Source)

        I have added PORTLET_ID as:

        public static final String PORTLET_ID = “1_WAR_notificationsportlet”;

        Is there a problem in portlet id ? As this is the portlet id for liferay notification portlet

    2. The Portlet ID you provided seems good enough and should work although I haven’t tried it myself.
      Make sure your payload contains userId that’s a valid Liferay User ID as that could also lead to a NullPointerException.

      When possible could you send me a sample of your payload and UserNotificationEventLocalServiceUtil call for verification?

      1. Hello Reigo,
        Null pointer exception is not appearing in console now and I am able to view the notification message but not able to view any information of the sender.
        Also I am able to view notification in default liferay homepage http://localhost:8080/home but unable to view any notification in custom site created.
        I am getting the an error in jsp as “userNotifications is null” while trying to view notification.
        here is the scriptlet of the jsp where error appears :
        var userNotifications = A.one(‘#portlet_1_WAR_notificationsportlet’);
        var userNotificationsList = userNotifications.one(‘.user-notifications-list-container .user-notifications-list’);

  14. HI Can you send me a code to my email id I need to look in to that code ans structure how u wrote,

    Regards,
    Ashokkumar

    1. Sorry Ashok but I don’t have that example project anymore and I have no time to create a new one at this point. The whole structure is standard Liferay Portlet project created by Liferay Eclipse Plugins.

  15. Hi,
    I have changed the portlet.xml file as below

    example
    Example
    com.NotificationPortlet

    view-template
    /view.jsp

    0

    text/html

    Example
    Example
    Example

    administrator

    guest

    power-user

    user

    and I have wrote NotificationPortlet class and put doSomethingGood method as below

    public void doSomethingGood(ActionRequest request, ActionResponse response)
    throws IOException, PortletException {

    ThemeDisplay themeDisplay =
    (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);
    long groupId = themeDisplay.getScopeGroupId();
    long yourCustomEntityId =
    ParamUtil.getLong(request, “yourCustomEntityId”);
    long userNotificationEventId =
    ParamUtil.getLong(request, “userNotificationEventId”);
    // Do the good thing you wanted to do…

    try {

    // Perhaps delete the notification now that the user has reacted?
    UserNotificationEventLocalServiceUtil.deleteUserNotificationEvent(userNotificationEventId);

    }
    catch (PortalException e) {
    }
    catch (SystemException e) {
    }
    }

    But I can not hit to doSomethingGood method. Is there another thing I need to do?

    1. Please post your code on how you are generating the URL for the action. This should be rather standard stuff and not related to the notification itself actually.
      Make sure you are creating the URL with the correct PortletID etc. Also instanceable portlet might require a more specific URL because you need to specify a specific instance ID to the PortletURL.

  16. Hola, me puedes apoyar por favor con el tema de las notificaciones, tengo 3 tipos de notificaciones, y quisiera que cada notificacion al hacer click me jale a distintos portlets, como puedo hacerlo por favor?

  17. Hi,

    This is a very useful and great post 🙂 . I have couple of question on this.

    1. getBody() method of code generates the HTML for custom notification. Is this the only way to achieve it?

    2. Say that, I want have a JSP page(detailed notifications, buttons etc.) for every notifications. How can I achieve this.

    1. 1) Yes, the Notification portlet calls getBody() method on your class and HOW you generate that HTML is pretty much up to you. Liferay’s own portlets also generate their content using this method.

      2) There is no good / simple way to have the HTML of the Notification to be rendered via JSP. The idea though is that you provide enough information to catch the Users attention and to quickly make a decision if they want to either “Click on it and show more”, “Press or some other Good button” or just ignore the notification.

      If you need to show a lot of information then actually you should just provide enough information for the user to understand what this notification is about and a or button on it so the user can open up your Portlet, which you can open in maximized state and then you can show anything you want, inside your portlet, in full screen. Plenty of space 🙂

  18. Hey man thank you for the knowledge! i’ve been trying to replicate this in my envyronment however i had no success. As the other guy stated up in the comments, there is a thing to do before use directly the portlet.xml example you are showing. I am facing that situation too, could you please tell how to link the portlet.xml with the user-notification-definitions.xml??

    I get the following error:
    “cvc-complex-type.2.4.a: Invalid content was found starting with element ‘user-notification-definitions’….” and my portlet.xml is pretty much a copy paste of what you’ve shown:

    example
    /icon.png
    example-user-notification-definitions.xml
    com.example.notifications.ExampleUserNotificationHandler

    any idea would be greatly taken! thank you pretty much!

    1. These important lines go into liferay-portlet.xml file, I think you put them inside portlet.xml 🙂

          <user-notification-definitions>example-user-notification-definitions.xml</user-notification-definitions>
          <user-notification-handler-class>com.example.notifications.ExampleUserNotificationHandler</user-notification-handler-class>		
      

      I’ve updated the post to make the important bits bold now.

  19. Dear Reigo,
    Thanks for your really helpful blog post. We’re now investigating the possibility that include the Liferay notification in our project.

    I now followed all the steps you’ve mentioned above. Everything else worked fine except the ICON part returns a null. See the screenshot I made here

    In the liferay-portlet.xml:

    example
    /icon.png
    example-user-notification-definitions.xml
    com.example.notifications.ExampleUserNotificationHandler


    I’ve tried place the icon.png to the root of my web content and all other possible locations with no success but a null. Could you please tell me where the problem is?

    Any comment will be appreciated.

    Liferay Version: liferay-portal-6.2-ce-ga2
    Notifications CE: 2.0.5

    1. Sounds like your Portlet Type does not result to a valid Portlet ID. Check out the last part of the post:

      NB Important Information: The com.example.notifications.ExampleUserNotificationHandler.PORTLET_ID string that you use as your notification type has to match an actual portlet ID. It doesn’t actually need to be YOUR portlet ID but that would be the right thing to have there. The reason being that Notifications display portlet uses it to display a small portlet icon next to your notification to help the user identify the source of the notification. Providing a bad Portlet ID or something like null leads to a hard-to-trace NullPointerException in the JSP. Took me an hour to track it down.

      Current master of notifications portlet:
      https://github.com/liferay/liferay-plugins/blob/master/portlets/notifications-portlet/docroot/WEB-INF/src/com/liferay/notifications/notifications/portlet/NotificationsPortlet.java
      is finding the icon like this:

      Portlet portlet = PortletLocalServiceUtil.getPortletById(themeDisplay.getCompanyId(), userNotificationEvent.getType());
      
      String portletName = portlet.getDisplayName();
      String portletIcon = portlet.getContextPath() + portlet.getIcon();
      

      From the screenshot it seems you are using “example_WAR_exampleportlet” as your Portlet ID. Make sure it matches your actual portlet ID because this one assumes you deployed example.war with exampleportlet inside of it…

  20. Hi, very nice article. I have a question about notification, do I need to create your own custom notification handler? Is not enough if I create notification by `UserNotificationEventLocalServiceUtil.addUserNotificationEvent`? I ask, because I have added several notifications, but neither does not display [screenshot]. Sorry for my english.

    1. Yes. You need to add that notification handler because liferay 6.2 by default does not show anything when you don’t have a proper handler for you message.

  21. Is that call to UserNotificationEventLocalServiceUtil.addUserNotificationEvent correct? The 2nd argument should be a type from UserNotificationDeliveryConstants shouldn’t it? At least that’s the usual bizarre way that Liferay does things. I swear, those guys must get bonuses for coming up with the most cumbersome API.

      1. Yes, after I posted that I decided you were right. Then, looking at the Social Office code I noticed there they use NotificationEventFactoryUtil.createNotificationEvent to build the NotificationEvent and then use the other add method, which seems easier. Thanks for burrowing through the Notification code and writing it up.

  22. Hi
    good doc. nicely understand by everyone.. i have implemented same thing in portal, its working fine and incrementing in docbar status but not shown notification to user when click on “view all notifications”….

    thanks
    madhu

    1. If you see the number increasing that means the proper user is getting a notification.
      If you can’t see the notification itself then that means that liferay was unable to build the notification HTML.
      This can happen when either your portlet ID is wrong or the payload does not contain a userId property.
      Try looking at the console when you open the notifications portlet, you might see a NullPointerException in there.

  23. Hi,
    It is working fine in liferay 6.2…
    when i implement it to liferay 6.1, it will not working…
    how to implement it in liferay6.1…

    Thanks
    Madhu

  24. Hello thank you for this tutorial , i am using your code in my JSF portlet (primefaces) but I can’t find how to use sendUserNotification(ActionRequest actionRequest,ActionResponse actionResponse) . How can i recover actionRequest and ActionResponse in my jsf portlet ?!

    best regards

    1. I have little experience with Liferay and JSF but I suggest you take a look at:
      https://raw.githubusercontent.com/liferay/liferay-faces/master/demos/showcase/showcase-portlet/src/main/java/com/liferay/faces/demos/portlet/ActionURLDemoPortlet.java

      I think you are refering to example in http://www.liferaysavvy.com/2014/12/liferay-dockbar-custom-user.html in which case I would say you don’t really need the ActionRequest, you just need to send add the notification via UserNotificationEventLocalServiceUtil.

      Here’s an example on how to do construct the notification without ServiceContext.

      		Date now = new Date();
      		User user = UserLocalServiceUtil.getUser(ID_WHO_TO_SEND_THE_NOTIFICATION);
      
      		JSONObject payloadJSON = JSONFactoryUtil.createJSONObject();
      		payloadJSON.put("userId", user.getUserId());
      		payloadJSON.put("otherExampleData", 123);
      
      		UserNotificationEvent userNotificationEvent = UserNotificationEventUtil.create(CounterLocalServiceUtil.increment());
      		userNotificationEvent.setUuid(UUID.randomUUID().toString());
      		userNotificationEvent.setCompanyId(user.getCompanyId());
      		userNotificationEvent.setUserId(user.getUserId());
      		userNotificationEvent.setType(YOUR_REAL_PORTLET_ID_MUST_BE_HERE);
      		userNotificationEvent.setTimestamp(now.getTime());
      		userNotificationEvent.setDeliverBy(now.getTime());
      		userNotificationEvent.setDelivered(false);
      		userNotificationEvent.setPayload(payloadJSON.toString());
      		userNotificationEvent.setArchived(false);
      
      		UserNotificationEventLocalServiceUtil.addUserNotificationEvent(userNotificationEvent);
      
      1. Thank you, now i can send notification but when i open a notification list on my dockbar i see this message : “Dockbar Notifications is temporarily unavailable”, and server provides this exception :

        java.lang.NullPointerException
        at com.liferay.taglib.util.IncludeTag.cleanUpSetAttributes(IncludeTag.java:182)

        Another question : what about showDetails method ?

  25. @kyle: Most probably your PORTLET_ID in the notification is just wrong. Please double-check it. showDetails is an example action, it’s just part of the URL that will be shown when you click on the notification.

    1. Thank you 🙂
      I created a new project and it works well, now the only problem is with the icon that always returns null. I tried to change tomcat-7.0.42 \ webapps \-notifications portlet \ Notifications \ view_entries.jsp but nothing changed.
      who has an idea how to fixed the icon.
      best regards

  26. Hi Reigo,

    I’m working on version 6.1.x.
    So, How can I implement this functionality in it?
    Or if This implementation is not possible in it then please tell me any other way to do so in this version.

    Thanks,
    Varun Jain

  27. @kyle and others with null icon problems, or with cleanUpSetAttributes problems

    If you go to

    notifications-portlet\notifications\

    and edit this file: view_entries.jsp

    and you find the text: “liferay-portlet:icon-portlet”

    and you edit that section of code like this:

        <liferay-portlet:icon-portlet portlet="<%=portlet>" />
    					
    

    You will fix some errors like that.

    Now can anyone help me make this system send emails, I have a notification up for a week for a user that never logged in the app, the email server is configured correctly. IT DOES NOT SEND 🙁

  28. @kyle and others with null icon problems, or with cleanUpSetAttributes problems

    If you go to

    notifications-portlet\notifications\

    and edit this file: view_entries.jsp

    and make this edit:

    http://postimg.org/image/keu7sfo17/9f6ea123/

    It should work

    Can anyone help me make this plugin send emails. I have users that have not logged in the app for a week but they won’t get any emails.

    1. While this will make it work, I would suggest you NOT modify the Liferay Notifications portlet as it’s available from the Marketplace and gets Updated from time-to-time so you would have to continue maintaining your modifications through the newer versions.

      The correct solution would be to get your notifications have a proper Portlet ID attached so that the notification portlet would actually find the right icon for you and everything would be working.

  29. Hi,

    First of all thanks for your code.

    Is it possible to have this integrated with Webservice. For example I have a webservice, which pulls feeds in JSON format from some source. Now is it possible to show notification for such feeds. Suppose if the feed gives new set of data, user is notified in the website.

    1. Absolutely! But it depends a bit of your webservice. In reality you can create notifications from pretty much any part of code that’s running inside Liferay. If your webservice is a stand-alone webapp just running on the same tomcat then you have a slight problem since you can’t see the Liferay user notification service classes.

      Liferay doesn’t provide a public json webservice for creating the notifications via third-party apps or random javascript code.
      BUT
      It’s rather trivial to make one yourself since all the code you need is on this page already 🙂

  30. Hi Reigo, Great Article!!!
    I follow above step to send friend request notification. I am able to add notification into “usernotificationevent” table. But notification counter not appear on dockbar.
    In there any other configuration.

    1. Nothing further than on this post.
      Make sure your notification is addressed to the right person?
      Maybe the notification is marked as read already?

  31. Hi, I know this blog post is quite old but I got a question : I want to add a struts action via a hook (like it’s explained here) and in this new struts action I would like to make custom notifications. Where shall I put those tags :
    user-notification-definitions

    and

    user-notification-handler-class

    as they shall be in liferay-portlet.xml but in a hook plugin I don’t have this file.

    1. You can add custom notifications but I don’t think you can “hook” a custom user notification handler in like that.
      I would suggest that you bind that to your own portlet.

  32. Hi Reigo,

    If I send a user a notification with deliverBy set to their userId when that user is not logged in, it is removed when they do log in (if I impersonate them it shows up and is then persisted when they do actually log in). Setting deliverBy to 0 resolved this problem for me. Do you have any insight as to why? Does this also happen for you? (It may be a result of also having Social Office installed)

    Thanks!

      1. Ah okay, in your example on how to add a new notification event you do (4th param):
        UserNotificationEventLocalServiceUtil.addUserNotificationEvent(user.getUserId(),
        com.example.notifications.ExampleUserNotificationHandler.PORTLET_ID,
        (new Date()).getTime(),
        user.getUserId(),
        payloadJSON.toString(),
        false, serviceContext);

  33. Hello Reigo,

    This is really Helpful and Unique Article, and I managed to make it work for New connection (Friend Request). Its just a simple message with a link to open a Friends Portlet for viewing Connections
    But here i am not getting how to make viewed notification message to “archived ” state so that viewed messages should not be visible in Dockbar Notification Icon.

    Also i am confused with below line
    viewURL.setParameter(“yourCustomEntityId”, String.valueOf(yourCustomEntityId));
    I mean what will be the “yourCustomEntityId” in my case?

    1. You can leave that out – it’s for demonstrative purposes only if your notification is related to your own made custom entities.

  34. Hello Reigo,
    I am facing same the problem that I am not getting how to make viewed notification message to “archived ” state so that viewed messages should not be visible in Dockbar Notification Icon

    1. Typically liferay notifications have a small X in the top-right corner to mark the notification as read.
      Once you click that then the notification is only visible in the all notifications list.

  35. Dear Reigo,
    Thanks for your really helpful blog post, it really helped me out.
    Now I’m wondering how can I send the notification by email using the ${com.liferay.portal.model.UserNotificationDeliveryConstants.TYPE_EMAIL}

  36. Hi Reigo,
    I didn’t get idea about how to create notification for Liferay Service class ? I want to add notification when WikiPage is updated or Comment on WikiPage. How to implement it?
    Please give me idea.

  37. Hi Reigo,
    great article. Very helpful! I have customized the portlet. Have you ever thought of a Rest API? I have some portlet that now can use my customization, and i have external webapp that i wish they called my portlet. Do you have any suggestions?
    Thank you very much.
    Riccardo

  38. Hi Reigo,
    I’d also like to use the liferay notification feature following your tutorial. And as many people before, I succeeded in increasing the number of notifications, but the notification message is not displayed. I tried to check by adding log-output whether the methods (getBody, getLink…) are called and : they are not called at all, not even the constructor of the UserNotificationHandler is called.
    So I conclude that my notification is written to the database, but my UserNotificationHandler class is not found. I don’t know why, perhaps you can give me a hint:
    In my project, I put the user-notification-definitions into project/src/main/resources. The liferay-portlet.xml is in project/src/main/webapp/WEB-INF. And the UserNotificationHandler in project/src/main/java/com/myproject/portal/notifications in the package com.myproject.portal.notifications. I wrote something like that into the liferay-portlet.xml:

    user-notification-definitions.xml

    com.myproject.portal.notifications.UserNotificationHandler

    Do you see a problem already?

    A second and less important question: how do I add a request instead of a notification?

    Thanks!

    1. Most probably your notification type ID and portlet ID does not match up. Read through all the comments on this page also – there’s more details about that one.

  39. Hi, I know this is probably an old post but I have a question. I got the code to work, thank you for this example on this lacking feature. But, is there any way I can add a notification that pops on a specific date? I’m working on an event manager, so when I add an event to a calendar, say in two days, I want that the notification will only be displayed in two days and not right away.

    Even if I set the timestamp it always show the notification right away. I hope my question makes sense. Thanks again for sharing this.

    1. Yes, the timestamp fields are used for other purposes. You can instead create a scheduled task that will be called periodically (say, every hour?) and create the notification at the moment when appropriate.

      import com.liferay.portal.kernel.messaging.Message;
      import com.liferay.portal.kernel.messaging.MessageListener;
      import com.liferay.portal.kernel.messaging.MessageListenerException;
       
      public class MyCronTask implements MessageListener { 
          //This method will be called periodically according to configuration
          @Override
          public void receive(Message message) throws MessageListenerException {
              System.out.println("Check timestamp and create notification when needed.");
          } 
      }
      


      And register it in liferay-portlet.xml file. Below is the sample example:

      <scheduler-entry>
          <scheduler-event-listener-class>com.codeyouneed.demo.MyCronTask</scheduler-event-listener-class>
          <trigger>
              <cron>
                  <cron-trigger-value>0 30 06 ? * MON-FRI</cron-trigger-value>
              </cron>
          </trigger>
      </scheduler-entry>
      
      

    1. You’ll have to create your own portlet project to implement your own notifications. You can’t just “add” it to random portlet.

  40. Hi,
    Thx for the tutorial… very helpfull.

    I would like to create a portlet with my own view using the liferay notification system. For this, I would have liked to hook the view JSP of the portlet but I can’t cause the notification portlet isn’t native liferay portlet. It was added to the bundle… like calendar portlet.

    Do you know how can I perform this using the portlet containing the UserNotificationHandler class please?!

    Thx in advance

    Mayes

    1. If you want to make your own notification display portlet, then just create a new portlet and show the notifications in there.
      Read the source of the notifications portlet and use their service classes to list the notifications from the database and call the render method similarly like they did it.

      The notifications portlet cannot be modified via hooks.

  41. Hi,
    thank you for a great blog post. I succeeded to create custom notifications following your example but
    I cannot get the notifications settings (Notification Delivery settings) to work for my entity.
    Does it work with your example?
    Are you sure
    ${com.example.notifications.ExampleUserNotificationHandler.PORTLET_ID}
    is correct, shouldn’t it be
    ${com.liferay.compat.portal.kernel.notifications.UserNotificationDefinition.NOTIFICATION_TYPE_ADD_ENTRY} or NOTIFICATION_TYPE_UPDATE_ENTRY?
    Best regards,
    Andreas

    1. The delivery settings probably only work regarding enabling / disabling the notifications portlet. Delivery via e-mail is not actually properly implemented in 6.2 and you should write a custom code to check if the user has requested an e-mail and send it. Liferay won’t do that for you.

  42. Hi Reigo Reinmets,
    In Liferay’s notification portlet power user can receive notification for “Rejected Posts”.
    But I want notification for “Approved Posts” also.
    How can I do this?
    I got notification count for “Approved posts” but didn’t get notification entry in popup.

    I am using this below code :

    try {
    String notificationText = “Your post has been approved.”;
    JSONObject payloadJSON = JSONFactoryUtil.createJSONObject();
    payloadJSON.put(“userId”, userId);
    payloadJSON.put(“notificationText”, notificationText);
    UserNotificationEventLocalServiceUtil.addUserNotificationEvent(userId,”2_WAR_notificationsportlet”,(new Date()).getTime(), userId,payloadJSON.toString(),false, serviceContext);
    } catch (Exception e) {
    _log.error(“Error in sending User Notifications” +e);
    }

    Please guide me. How can I get notification entry for “Approved Posts”.

    1. If the number increases but you cannot see it, then your handler fails to render properly.
      Your code seems to be OK so I would start looking if your notification-definitions.xml is looking correct and add some debugging lines to your Handler class getBody and getLink methods.

      If you can see that the log statements at all, then your handler is not invoked so the notification handler is not configured properly.

      One idea I had is that you are using the Portlet ID of the Notifications portlet but it actually should be the ID of the portlet that is registering the handler in liferay-portlet.xml.

  43. Hi there,

    I implemented this solution and I used a custom portlet to add notifications. However. When I access the notifications of user and try to configure notifications, I am getting: Can’t find bundle for base name content.Language, locale en_US. I am using Liferay 7.0

    1. This whole guide is for Liferay 6.2 and should not be followed directly for 7.0 as things have changed a bit.
      The error you are getting is about a translation file being missing, so make sure your bundle contains those language files.

  44. Hi,

    We are using UserNotificationEvent to send the notifications. These notifications are also visible however the content of notification is “null” or “()” This happens with Liferay 6.2 and GA4.

    However the notifications are properly seen with Liferay 6.2 and GA2.

    We are unable to send notifications through scheduler as could not get servicecontext.

Leave a Reply

Your email address will not be published. Required fields are marked *