Saturday, November 1, 2014

Customizing workflows in WSO2 API Manager

In WSO2 API Manager, Workflow extensions allow you to attach a custom workflow to various operations in the API Manager for

  • User Signup
  • Application Creation
  • Application Registration
  • Subscription


By default, the API Manager workflows have Simple Workflow Executor engaged in them. The Simple Workflow Executor carries out an operation without any intervention by a workflow admin. For example, when the user creates an application, the Simple Workflow Executor allows the application to be created without the need for an admin to approve the creation process.

In order to enforce intervention by a workflow admin, you can engage the WS Workflow Executor. It invokes an external Web service when executing a workflow and the process completes based on the output of the Web service. For example, when the user creates an application, the request goes into an intermediary state where it remains until authorized by a workflow admin.

You can try out the default workflow extensions provided by WSO2 API Manager to engage business processes with API management operations as described in here

There are two extension points exposed with WSO2 API Manager to customize workflows.

Customizing the Workflow Executor
  • When you need to change the workflow logic
  • When you need to change the Data Formats


Customizing the Business Process
  • When you are happy with the Data Formats and need to change only the business flow
This blog post will provide a sample on how we can customize workflow executors and change the workflow logic.

First let's look at WorkflowExecutor class which each WS workflow executor is extended from.

/**
 * This is the class that should be extended by each workflow executor implementation.
 */
public abstract class WorkflowExecutor {

/**
 * The Application Registration Web Service Executor.
 */
public class ApplicationRegistrationWSWorkflowExecutor extends WorkflowExecutor{

//Logic to execute the workflow
public void execute(WorkflowDTO workflowDTO) { }

//Logic to complete the workflow
public void complete(WorkflowDTO workflowDTO) { }

//Returns the workflow type - ex: WF_TYPE_AM_USER_SIGNUP
public String getWorkflowType() { }

//Used to get workflow details
public List getWorkflowDetails(String workflowStatus) { }

}


As the example scenario, let's consider the Application registration workflow of WSO2 API manager.
After an application is created, you can subscribe to available APIs, but you get the consumer key/secret and access tokens only after registering the application. There are two types of registrations that can be done to an application: production and sandbox. You change the default application registration workflow in situations such as the following:


  • To issue only sandbox keys when creating production keys is deferred until testing is complete.
  • To restrict untrusted applications from creating production keys. You allow only the creation of sandbox keys.
  • To make API subscribers go through an approval process before creating any type of access token.
Find step by step instructions on how we can configure Application Registration Workflow from here




Sending an email to Administrator upon Application Registration

As the extension of this Application Registration workflow, we are going customize the workflow executor and send an email to Administrator once the workflow is triggered

1. First write a new executor extending ApplicationRegistrationWSWorkflowExecutor

public class AppRegistrationEmailSender extends 
ApplicationRegistrationWSWorkflowExecutor {

2. Add private String attributes and public getters and setters for email properties (adminEmail, emailAddress, emailPassword)

        
        private String adminEmail;
 private String emailAddress;
 private String emailPassword;
        public String getAdminEmail() {
  return adminEmail;
 }

 public void setAdminEmail(String adminEmail) {
  this.adminEmail = adminEmail;
 }

 public String getEmailAddress() {
  return emailAddress;
 }

 public void setEmailAddress(String emailAddress) {
  this.emailAddress = emailAddress;
 }

 public String getEmailPassword() {
  return emailPassword;
 }

 public void setEmailPassword(String emailPassword) {
  this.emailPassword = emailPassword;
 }

3. Override execute(WorkflowDTO workflowDTO) method and implement email sending logic. Finally invoke super.execute(workflowDTO).

        @Override
 public void execute(WorkflowDTO workflowDTO) throws WorkflowException {
   
  ApplicationRegistrationWorkflowDTO appDTO = (ApplicationRegistrationWorkflowDTO) workflowDTO;
  
  String emailSubject = appDTO.getKeyType() + "Application Registration";

  String emailText = "Appplication " + appDTO.getApplication().getName() + " is registered for " + 
    appDTO.getKeyType() + " key by user " + appDTO.getUserName();
  
  try {
   EmailSender.sendEmail(emailAddress, emailPassword, adminEmail, emailSubject, emailText);
  } catch (MessagingException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
  //SEND EMAIL
  super.execute(workflowDTO);
  
 }


Find the complete source code of custom workflow executor. 
 
package org.wso2.sample.workflow;

import javax.mail.MessagingException;

import org.wso2.carbon.apimgt.impl.dto.ApplicationRegistrationWorkflowDTO;
import org.wso2.carbon.apimgt.impl.dto.WorkflowDTO;
import org.wso2.carbon.apimgt.impl.workflow.ApplicationRegistrationWSWorkflowExecutor;
import org.wso2.carbon.apimgt.impl.workflow.WorkflowException;

public class AppRegistrationEmailSender extends ApplicationRegistrationWSWorkflowExecutor {
 
 private String adminEmail;
 private String emailAddress;
 private String emailPassword;
 

 @Override
 public void execute(WorkflowDTO workflowDTO) throws WorkflowException {
   
  ApplicationRegistrationWorkflowDTO appDTO = (ApplicationRegistrationWorkflowDTO) workflowDTO;
  
  String emailSubject = appDTO.getKeyType() + "Application Registration";

  String emailText = "Appplication " + appDTO.getApplication().getName() + " is registered for " + 
    appDTO.getKeyType() + " key by user " + appDTO.getUserName();
  
  try {
   EmailSender.sendEmail(emailAddress, emailPassword, adminEmail, emailSubject, emailText);
  } catch (MessagingException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
  //SEND EMAIL
  super.execute(workflowDTO);
  
 }
    
 public String getAdminEmail() {
  return adminEmail;
 }

 public void setAdminEmail(String adminEmail) {
  this.adminEmail = adminEmail;
 }

 public String getEmailAddress() {
  return emailAddress;
 }

 public void setEmailAddress(String emailAddress) {
  this.emailAddress = emailAddress;
 }

 public String getEmailPassword() {
  return emailPassword;
 }

 public void setEmailPassword(String emailPassword) {
  this.emailPassword = emailPassword;
 }

}

Now modify the existing ProductionApplicationRegistration as below.
 

        admin@wso2.com
        admin@gmail.com  
        admin123
        http://localhost:9765/services/ApplicationRegistrationWorkFlowProcess/
        admin
        admin
        https://localhost:8248/services/WorkflowCallbackService


You can do the same modification to SandboxApplicationRegistration workflow as below.
 

        admin@wso2.com
        admin@gmail.com  
        admin123
        http://localhost:9765/services/ApplicationRegistrationWorkFlowProcess/
        admin
        admin
        https://localhost:8248/services/WorkflowCallbackService


With this change, Application Registration workflows will trigger the workflows through AppRegistrationEmailSender which will send an email to adminEmail email address. Then it will invoke the default ApplicationRegistrationWSWorkflowExecutor. 

2 comments:

  1. Hello, I'm trying to customize a user signup extension in API manager in the same way and sending email doesnt work. I keep getting a workflow exception with message: Couldn't connect to SMTP host: smtp.gmail.com, port 587
    Here is the code of my extended email sender class:

    package be.i8c.workflows;

    import java.util.Properties;

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

    import org.wso2.carbon.apimgt.impl.dto.WorkflowDTO;
    import org.wso2.carbon.apimgt.impl.workflow.UserSignUpWSWorkflowExecutor;
    import org.wso2.carbon.apimgt.impl.workflow.WorkflowException;

    public class UserSignupEmailSender extends UserSignUpWSWorkflowExecutor{

    private static final long serialVersionUID = 7918254751662763705L;

    private String adminEmail;

    private String emailAddress;

    private String emailPassword;

    @Override
    public void execute(WorkflowDTO workflowDTO) throws WorkflowException{
    Properties props = new Properties();
    props.put("mail.smtp.auth", "true");
    props.put("mail.smtp.starttls.enable", "true");
    props.put("mail.smtp.host", "smtp.gmail.com");
    props.put("mail.smtp.port", "587");

    Session session = Session.getInstance(props,
    new javax.mail.Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
    return new PasswordAuthentication(emailAddress,
    emailPassword);
    }
    });

    try {

    Message message = new MimeMessage(session);
    message.setFrom(new InternetAddress(emailAddress));
    message.setRecipients(Message.RecipientType.TO,
    InternetAddress.parse(adminEmail));
    message.setSubject("User Signup");
    message.setText("User signed up : " + workflowDTO.getWorkflowReference());

    Transport.send(message);
    System.out.println("Sent email to notify subscription creation");
    super.execute(workflowDTO);

    } catch (MessagingException e) {
    e.printStackTrace();
    throw new WorkflowException(e.getMessage());
    } catch (Exception e){
    e.printStackTrace();
    throw new WorkflowException(e.getMessage());
    }

    }

    public String getAdminEmail() {
    return adminEmail;
    }

    public void setAdminEmail(String adminEmail) {
    this.adminEmail = adminEmail;
    }

    public String getEmailAddress() {
    return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
    this.emailAddress = emailAddress;
    }

    public String getEmailPassword() {
    return emailPassword;
    }

    public void setEmailPassword(String emailPassword) {
    this.emailPassword = emailPassword;
    }

    -----
    Im using a valid gmail address and password for emailAddress and emailPassword but it doesnt work..Is there something that I should change in configuration files? Another question is: I would like to include an email address of the signed up user in my email. How can I get this information about the user?

    ReplyDelete
  2. I have read your blog its very attractive and impressive. I like it your blog.

    Java Training in Chennai Core Java Training in Chennai Core Java Training in Chennai

    Java Online Training Java Online Training JavaEE Training in Chennai Java EE Training in Chennai

    ReplyDelete