Writing WCM custom workflow email actions-(Custom WCM Workflow Notification Email)-Part3

1.     Define custom workflow action factory

package com.sivavaka.wcm.custom.workflow;

import java.util.Locale;
import com.ibm.workplace.wcm.api.Document;
import com.ibm.workplace.wcm.api.custom.CustomWorkflowAction;
import com.ibm.workplace.wcm.api.custom.CustomWorkflowActionFactory;

public class SivaTestCustomWcmEmailBodyWorkFlowActionFactory implements CustomWorkflowActionFactory {

    public String[] getActionNames() {
           String names[] = { "sendEmailToApprovers", "sendEmailToAuthors", "sendEmailToOwners" };

           return names;

    public String getActionTitle(Locale arg0, String arg1) {
           if (arg1.equalsIgnoreCase("sendEmailToApprovers")) {
                   return "sendEmailToApprovers";
           } else if (arg1.equalsIgnoreCase("sendEmailToAuthors")) {
                   return "sendEmailToAuthors";
           } else if (arg1.equalsIgnoreCase("sendEmailToOwners")) {
                   return "sendEmailToOwners";
           return null;
    public String getActionDescription(Locale arg0, String arg1) {

           if (arg1.equalsIgnoreCase("sendEmailToApprovers")) {
                   return "Send Email To Approvers with custom Body";
           } else if (arg1.equalsIgnoreCase("sendEmailToAuthors")) {
                   return "Send Email To Authors with custom Body";
           } else if (arg1.equalsIgnoreCase("sendEmailToOwners")) {
                   return "Send Email To Oweners with custom Body";
           return "";
    public CustomWorkflowAction getAction(String arg0, Document arg1) {

           if (arg0.equalsIgnoreCase("sendEmailToApprovers")) {
                   return new SivaTestSendEmailToApproversWithCustomBodyWorkFlowAction();
           } else if (arg0.equalsIgnoreCase("sendEmailToAuthors")) {
                   return new SivaTestSendEmailToAuthorsWithCustomBodyWorkFlowAction();
           } else if (arg0.equalsIgnoreCase("sendEmailToOwners")) {
                   return new SivaTestSendEmailToOwnersWithCustomBodyWorkFlowAction();

           return null;
    public String getName() {
           return "SivaTestCustomWcmEmailBodyWorkFlowActionFactory";

    public String getTitle(Locale arg0) {
           return "Siva Test Customized WCM Notification Email Body ";


2.     Define separate workflow actions to send emails to authors, approvers and owners.

Below is for the custom action to send email to approvers (similar define other two , can browse the code here)

package com.sivavaka.wcm.custom.workflow;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.naming.InitialContext;

import com.ibm.portal.um.Group;
import com.ibm.portal.um.Principal;
import com.ibm.portal.um.PumaController;
import com.ibm.portal.um.PumaHome;
import com.ibm.portal.um.PumaLocator;
import com.ibm.portal.um.PumaProfile;
import com.ibm.portal.um.User;
import com.ibm.workplace.wcm.api.Content;
import com.ibm.workplace.wcm.api.Document;
import com.ibm.workplace.wcm.api.WebContentCustomWorkflowService;
import com.ibm.workplace.wcm.api.Workspace;
import com.ibm.workplace.wcm.api.custom.CustomWorkflowAction;
import com.ibm.workplace.wcm.api.custom.CustomWorkflowActionResult;
import com.ibm.workplace.wcm.api.custom.Directive;
import com.ibm.workplace.wcm.api.custom.Directives;
import com.sivavaka.mail.util.SivaTestSendEmail;

public class SivaTestSendEmailToApproversWithCustomBodyWorkFlowAction implements CustomWorkflowAction {

    InitialContext initContext = null;
    WebContentCustomWorkflowService webContentCustomWorkflowService = null;
    CustomWorkflowActionResult result = null;

    // PUMA Objects
    PumaHome pHome = null;
    PumaController pController = null;
    PumaLocator pLocator = null;
    PumaProfile pProfile = null;

    Principal approverPrincipal = null;
    Content content = null;
    String message = null;

    public SivaTestSendEmailToApproversWithCustomBodyWorkFlowAction() {
           System.out.println("Inside the SivaTestSendEmailToApproversWithCustomBodyWorkFlowAction.... ");

    public CustomWorkflowActionResult execute(Document arg0) {
           System.out.println("Executing SivaTestSendEmailToApproversWithCustomBodyWorkFlowAction.... ");
           Directive directive = Directives.CONTINUE;

           if (arg0 instanceof Content) {
                   try {

                          content = (Content) arg0;
                          Workspace ws = content.getSourceWorkspace();
                          initContext = new InitialContext();
                          //PUMA related
                          pHome = (com.ibm.portal.um.PumaHome) initContext.lookup(com.ibm.portal.um.PumaHome.JNDI_NAME);
                          pController = pHome.getController();
                          pLocator = pHome.getLocator();
                          pProfile = pHome.getProfile();

                          //User Attributes
                          List<String> userAttributes = new ArrayList<String>();

                          List<Group> groupsList = new ArrayList<Group>();
                          Set<User> usersList = new HashSet<User>();
                          List<Principal> membersList = null;
                          List<Map<String, Object>> approversDetails = new ArrayList<Map<String, Object>>();
                          //Below additional logic is check for for bother users and groups as approvers,
                          String currentApprovers[] = content.getCurrentApprovers();
                          //create a separate lists for users and groups
                          for (int i = 0; i < currentApprovers.length; i++) {
                          //find all users from groups and add it to users list
                          for(Group group : groupsList){
                                  membersList = pLocator.findMembersByGroup(group,false);
                                  for (Principal member : membersList) {
                                         usersList.add((User) member);
                          //Get required attributes from users list
                          for(User user : usersList){
                          for (Map<String, Object> userDetail : approversDetails) {
                                  System.out.println("Username::" + userDetail.get("cn")+ "email::" + userDetail.get("ibm-primaryEmail"));
                          //call mail utility method to send email.
                          SivaTestSendEmail.sendEmail(approversDetails, content);

                   } catch (Exception ex) {
                          message = "An exception has occured " + ex.getMessage();
                          directive = Directives.ROLLBACK_DOCUMENT;
                   try {

                          webContentCustomWorkflowService = (WebContentCustomWorkflowService) initContext.lookup("portal:service/wcm/WebContentCustomWorkflowService");
                   } catch (Exception ex) {
                          message = "An exception has occured in ex " + ex.getMessage();
                          directive = Directives.ROLLBACK_DOCUMENT;

           result = webContentCustomWorkflowService.createResult(directive,message);

           return result;

    public Date getExecuteDate(Document arg0) {
           return DATE_EXECUTE_NOW;

3.     Define plugin.xml

<?xml version="1.0" encoding="UTF-8"?>
<plugin id= "com.ibm.workplace.wcm.api.custom" name= "Sample Custom Workflow Action Factory" version= "1.0.0" provider-name= "IBM" >
<extension-point id= "CustomWorkflowActionFactory" name="CustomWorkflowActionFactory" />
<extension point="com.ibm.workplace.wcm.api.CustomWorkflowActionFactory" id= "SivaTestCustomWcmEmailBodyWorkFlowActionFactory" >
    <provider class= "com.sivavaka.wcm.custom.workflow.SivaTestCustomWcmEmailBodyWorkFlowActionFactory"/>


4.     Write Java Mail utility to send email to list of users

This is simple mail utility , you can customize to make it production ready (like reading it from properties files …etc)
package com.sivavaka.mail.util;
import java.util.*;

import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import com.ibm.workplace.wcm.api.Content;

public class SivaTestSendEmail {
    public static void sendEmail(List<Map<String, Object>> usersDetails, Content content){
           String from = "wcmsupport@mail.sivavaka.com";
           final String SMTP_AUTH_USER = "admin@mail.sivavaka.com";
           final String SMTP_AUTH_PASSWORD = "password";
           Properties properties = new Properties();
           properties.setProperty("mail.smtp.host", "smtp.sivavaka.com");
           properties.setProperty("mail.smtp.port", "25");
           properties.setProperty("mail.smtp.auth", "true");
           Session session = Session.getInstance(properties, new Authenticator() {
               protected PasswordAuthentication getPasswordAuthentication() {
                   return new PasswordAuthentication(SMTP_AUTH_USER, SMTP_AUTH_PASSWORD);
           MimeMessage message = null;
           String approverEmail = "";
           for(Map<String, Object> user : usersDetails){
                   approverEmail = (String)user.get("ibm-primaryEmail");
                   if(null != approverEmail && 0 < approverEmail.trim().length()){
                                  message = new MimeMessage(session);
                                  message.setFrom(new InternetAddress(from));
                                  message.addRecipient(Message.RecipientType.TO, new InternetAddress(approverEmail));
                                  message.setSubject("Workflow Notification :: " + content.getTitle());
                                  message.setContent(customEmailBody(user, content), "text/html" );
                          }catch(Exception e){
    public static String customEmailBody(Map<String, Object> user, Content content) throws Exception{
           StringBuffer emailBody = new StringBuffer();
           emailBody.append("Hi " + user.get("cn") +",");
           emailBody.append("The content '"+content.getTitle()+"' has moved to Workflow Stage '"+(content.getWorkflowStageId()).getName()+"." );
           emailBody.append("Click on the following link to review the item, <a href='https://sivapc.sivavaka.com/wps/myportal/wcmAuthoring?wcmAuthoringAction=read&docid=com.aptrix.pluto.content.Content/"+(content.getId()).getId()+"'>"+content.getTitle()+"</a>");
           emailBody.append("Thanks<br/>WCM Team");
           return emailBody.toString();


5.     Install the ear file application

Once application is installed from WAS console (using wsadmin scripts) will see something like below.

[12/22/13 7:57:40:678 EST] 00000036 AdminHelper   A   ADMN1008I: An attempt is made to start the CustomWcmEmailBodyWorkFlowActionEAR application. (User ID = defaultWIMFileBasedRealm/wasadmin)
[12/22/13 7:57:40:745 EST] 00000036 CompositionUn A   WSVR0190I: Starting composition unit WebSphere:cuname=CustomWcmEmailBodyWorkFlowActionEAR in BLA WebSphere:blaname=CustomWcmEmailBodyWorkFlowActionEAR.
[12/22/13 7:57:41:212 EST] 00000036 ExtensionPoin I   CWXRS0034I: Extension com.ibm.workplace.wcm.api.custom.SivaTestCustomWcmEmailBodyWorkFlowActionFactory connected with Extension Point com.ibm.workplace.wcm.api.CustomWorkflowActionFactory
[12/22/13 7:57:41:370 EST] 00000036 webapp        I com.ibm.ws.webcontainer.webapp.WebGroupImpl WebGroup SRVE0169I: Loading Web Module: CustomWcmEmailBodyWorkFlowAction.
[12/22/13 7:57:41:386 EST] 00000036 WASSessionCor I SessionContextRegistry getSessionContext SESN0176I: Will create a new session context for application key default_hostCustomWcmEmailBodyWorkFlowAction
[12/22/13 7:57:41:396 EST] 00000036 webcontainer  I com.ibm.ws.wswebcontainer.VirtualHost addWebApplication SRVE0250I: Web Module CustomWcmEmailBodyWorkFlowAction has been bound to default_host[*:80,*:443,*:10000,*:10002,*:10039,*:10029,*:10032].
[12/22/13 7:57:41:402 EST] 00000036 ApplicationMg A   WSVR0221I: Application started: CustomWcmEmailBodyWorkFlowActionEAR
[12/22/13 7:57:41:404 EST] 00000036 CompositionUn A   WSVR0191I: Composition unit WebSphere:cuname=CustomWcmEmailBodyWorkFlowActionEAR in BLA WebSphere:blaname=CustomWcmEmailBodyWorkFlowActionEAR started

Download/Browse the Sample Code


  1. Hi Siva,
    Thanks for the nice article.
    I have created custom action application as you said above. And observed that extension point for my application is created. But when I try to create a custom workflow action it gives me the following error message
    "No custom workflow actions have been registered".

    Help me find the solution.

  2. Try downloading EAR and deploy directly and see if wcm authoring shows the custom actions (while creating workflow action in authoring)

  3. I downloaded and installed your EAR file.
    And tried creating the custom action it still shows the same error message.



  4. Make sure you cross checked all as mentioned in below troubleshooting article.


  5. Hi
    I have deployed the same application under my Virtual Machine and it works absolutely fine.
    How can I check why custom actions are not getting registered.

  6. hello Siva, I need to create an action to send to and specific user an email.
    Each content has a specific user to approve changes over it.
    Can i do that?

    1. This can be acheived without writing custom action isn't it. I mean in email action configuration you can configure whom to send email i.e. "authors/owners/approvers" of content.

  7. Thanks for the quick response :) , do you mean i should use the "property/access/Reviewer" atribute, to store a single user on the content instance and then retrieve this data in the workflow?

    1. Reviewers of content ususally comes from workflow stage and if you have "email action" in that stage it should generate email based on your selection