rapture.dp.invocable.notification.steps.NotificationStep.java Source code

Java tutorial

Introduction

Here is the source code for rapture.dp.invocable.notification.steps.NotificationStep.java

Source

/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2011-2016 Incapture Technologies LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package rapture.dp.invocable.notification.steps;

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.mail.MessagingException;
import javax.mail.internet.AddressException;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.google.common.net.MediaType;

import rapture.common.CallingContext;
import rapture.common.RaptureURI;
import rapture.common.api.AdminApi;
import rapture.common.api.DecisionApi;
import rapture.common.dp.AbstractInvocable;
import rapture.common.exception.ExceptionToString;
import rapture.common.impl.jackson.JacksonUtil;
import rapture.kernel.Kernel;
import rapture.kernel.dp.ExecutionContextUtil;
import rapture.mail.EmailTemplate;
import rapture.mail.Mailer;

// Should maybe subclass NotificationStep to provide other notification mechanisms?

// A step to notify the user
// * By email
// * By instant message app - Slack/What'sApp/Pidgin
// * text message?

public class NotificationStep extends AbstractInvocable {
    private static Logger log = Logger.getLogger(NotificationStep.class);
    DecisionApi decision;

    public NotificationStep(String workerUri, String stepName) {
        super(workerUri, stepName);
        decision = Kernel.getDecision();
    }

    @Override
    public String invoke(CallingContext ctx) {
        // Don't set STEPNAME here because we want the name of the preceding step
        // Can read config from a documemnt or pass as args
        AdminApi admin = Kernel.getAdmin();
        String types = StringUtils.stripToNull(decision.getContextValue(ctx, getWorkerURI(), "NOTIFY_TYPE"));

        if (types == null) {
            decision.writeWorkflowAuditEntry(ctx, getWorkerURI(),
                    "Problem in " + getStepName() + ": parameter NOTIFY_TYPE is not set", true);
            return getErrorTransition();
        }
        StringBuffer error = new StringBuffer();
        String retval = getNextTransition();
        for (String type : types.split("[, ]+")) {
            try {
                if (type.equalsIgnoreCase("SLACK") && !sendSlack(ctx)) {
                    decision.writeWorkflowAuditEntry(ctx, getWorkerURI(), "Slack notification failed", true);
                    retval = getErrorTransition();
                }
            } catch (Exception e) {
                Throwable cause = ExceptionToString.getRootCause(e);
                error.append("Cannot send slack notification : ").append(cause.getLocalizedMessage()).append("\n");
                decision.writeWorkflowAuditEntry(ctx, getWorkerURI(),
                        "Problem in " + getStepName() + ": slack notification failed", true);
                decision.writeWorkflowAuditEntry(ctx, getWorkerURI(), ExceptionToString.summary(cause), true);
                retval = getErrorTransition();
            }

            try {
                if (type.equalsIgnoreCase("EMAIL") && !sendEmail(ctx)) {
                    decision.writeWorkflowAuditEntry(ctx, getWorkerURI(), "Email notification failed", true);
                    retval = getErrorTransition();
                }
            } catch (Exception e) {
                Throwable cause = ExceptionToString.getRootCause(e);
                error.append("Cannot send email notification : ").append(cause.getLocalizedMessage()).append("\n");
                decision.writeWorkflowAuditEntry(ctx, getWorkerURI(),
                        "Problem in NotificationStep " + getStepName() + ": email notification failed", true);
                decision.writeWorkflowAuditEntry(ctx, getWorkerURI(), ExceptionToString.summary(cause), true);
                log.error(ExceptionToString.format(ExceptionToString.getRootCause(e)));
                retval = getErrorTransition();
            }
        }

        String errMsg = error.toString();
        if (!StringUtils.isEmpty(errMsg)) {
            log.error(errMsg);
            decision.setContextLiteral(ctx, getWorkerURI(), getStepName(), "Notification failure");
            decision.setContextLiteral(ctx, getWorkerURI(), getErrName(), errMsg);
        }
        return retval;
    }

    private int doPost(URL url, byte[] body) throws IOException {
        HttpURLConnection http = (HttpURLConnection) url.openConnection();
        http.setFixedLengthStreamingMode(body.length);
        http.setRequestProperty("Content-Type", MediaType.JSON_UTF_8.toString());
        http.setRequestMethod("POST");
        http.setDoOutput(true);
        http.connect();
        try (OutputStream stream = http.getOutputStream()) {
            stream.write(body);
        }
        int response = http.getResponseCode();
        http.disconnect();
        return response;
    }

    public String renderTemplate(CallingContext ctx, String template) {
        String workOrder = new RaptureURI(getWorkerURI()).toShortString();
        return ExecutionContextUtil.evalTemplateECF(ctx, workOrder, template, new HashMap<String, String>());
    }

    private boolean sendSlack(CallingContext ctx) throws IOException {
        String message = StringUtils.stripToNull(decision.getContextValue(ctx, getWorkerURI(), "MESSAGE_BODY"));
        // Legacy: use template if values are not set
        String templateName = StringUtils
                .stripToNull(decision.getContextValue(ctx, getWorkerURI(), "MESSAGE_TEMPLATE"));
        String webhook = StringUtils.stripToNull(decision.getContextValue(ctx, getWorkerURI(), "SLACK_WEBHOOK"));
        if (webhook == null) {
            decision.writeWorkflowAuditEntry(ctx, getWorkerURI(),
                    "Problem in " + getStepName() + ": No webhook specified", true);
            return false;
        }

        if (message == null) {
            if (templateName == null) {
                decision.writeWorkflowAuditEntry(ctx, getWorkerURI(),
                        "Problem in " + getStepName() + ": No message specified", true);
                return false;
            }
            EmailTemplate template = Mailer.getEmailTemplate(ctx, templateName);
            message = template.getMsgBody();
        }
        URL url = new URL(webhook);
        Map<String, String> slackNotification = new HashMap<>();
        slackNotification.put("text", renderTemplate(ctx, message));
        int response = doPost(url, JacksonUtil.bytesJsonFromObject(slackNotification));
        if (response == 200) {
            decision.writeWorkflowAuditEntry(ctx, getWorkerURI(),
                    getStepName() + ": slack notification sent successfully", false);
            return true;
        } else {
            decision.writeWorkflowAuditEntry(ctx, getWorkerURI(),
                    "Problem in " + getStepName() + ": slack notification failed with HTTP error code " + response,
                    true);
            return false;
        }
    }

    private boolean sendEmail(CallingContext ctx) throws AddressException, MessagingException {
        String message = StringUtils.stripToNull(decision.getContextValue(ctx, getWorkerURI(), "MESSAGE_BODY"));
        String subject = StringUtils.stripToNull(decision.getContextValue(ctx, getWorkerURI(), "MESSAGE_SUBJECT"));
        String recipientList = StringUtils
                .stripToNull(decision.getContextValue(ctx, getWorkerURI(), "EMAIL_RECIPIENTS"));
        // Legacy: use template if values are not set
        String templateName = StringUtils
                .stripToNull(decision.getContextValue(ctx, getWorkerURI(), "MESSAGE_TEMPLATE"));

        if (templateName != null) {
            EmailTemplate template = Mailer.getEmailTemplate(ctx, templateName);
            if (template != null) {
                if (message == null)
                    message = template.getMsgBody();
                if (subject == null)
                    subject = template.getSubject();
                if (recipientList == null)
                    recipientList = template.getEmailTo();
            }
        }

        if (message == null) {
            decision.writeWorkflowAuditEntry(ctx, getWorkerURI(),
                    "Problem in " + getStepName() + ": No message specified", true);
            return false;
        }

        if (recipientList == null) {
            decision.writeWorkflowAuditEntry(ctx, getWorkerURI(),
                    "Problem in " + getStepName() + ": No recipient specified", true);
            return false;
        }

        try {
            Mailer.email(renderTemplate(ctx, recipientList).split("[, ]+"), renderTemplate(ctx, subject),
                    renderTemplate(ctx, message));
            decision.writeWorkflowAuditEntry(ctx, getWorkerURI(),
                    getStepName() + ": email notification sent successfully", false);
            return true;
        } catch (MessagingException e) {
            log.warn("Unable to send email", e);
            return false;
        }
    }
}