it.eng.spagobi.tools.scheduler.dispatcher.MailDocumentDispatchChannel.java Source code

Java tutorial

Introduction

Here is the source code for it.eng.spagobi.tools.scheduler.dispatcher.MailDocumentDispatchChannel.java

Source

/* SpagoBI, the Open Source Business Intelligence suite
    
 * Copyright (C) 2012 Engineering Ingegneria Informatica S.p.A. - SpagoBI Competency Center
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0, without the "Incompatible With Secondary Licenses" notice. 
 * If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package it.eng.spagobi.tools.scheduler.dispatcher;

import it.eng.spago.security.IEngUserProfile;
import it.eng.spagobi.analiticalmodel.document.bo.BIObject;
import it.eng.spagobi.behaviouralmodel.analyticaldriver.bo.BIObjectParameter;
import it.eng.spagobi.commons.SingletonConfig;
import it.eng.spagobi.commons.dao.DAOFactory;
import it.eng.spagobi.commons.utilities.StringUtilities;
import it.eng.spagobi.tools.dataset.bo.IDataSet;
import it.eng.spagobi.tools.dataset.common.behaviour.UserProfileUtils;
import it.eng.spagobi.tools.dataset.common.datastore.IDataStore;
import it.eng.spagobi.tools.dataset.common.datastore.IField;
import it.eng.spagobi.tools.dataset.common.datastore.IRecord;
import it.eng.spagobi.tools.scheduler.to.DispatchContext;
import it.eng.spagobi.utilities.exceptions.SpagoBIRuntimeException;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Security;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;

import org.apache.commons.validator.GenericValidator;
import org.apache.log4j.Logger;

/**
 * @author Andrea Gioia (andrea.gioia@eng.it)
 *
 */
public class MailDocumentDispatchChannel implements IDocumentDispatchChannel {

    private DispatchContext dispatchContext;

    // logger component
    private static Logger logger = Logger.getLogger(MailDocumentDispatchChannel.class);
    final String DEFAULT_SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
    final String CUSTOM_SSL_FACTORY = "it.eng.spagobi.commons.services.DummySSLSocketFactory";

    public MailDocumentDispatchChannel(DispatchContext dispatchContext) {
        this.dispatchContext = dispatchContext;
        try {
            IEngUserProfile userProfile = this.dispatchContext.getUserProfile();
            //gets the dataset data about the email address
            IDataStore emailDispatchDataStore = null;
            if (dispatchContext.isUseDataSet()) {
                IDataSet dataSet = DAOFactory.getDataSetDAO()
                        .loadActiveDataSetByLabel(dispatchContext.getDataSetLabel());
                dataSet.setUserProfileAttributes(UserProfileUtils.getProfileAttributes(userProfile));
                dataSet.loadData();
                emailDispatchDataStore = dataSet.getDataStore();
            }
            dispatchContext.setEmailDispatchDataStore(emailDispatchDataStore);
        } catch (Throwable t) {
            throw new SpagoBIRuntimeException("Impossible to instatiate MailDocumentDispatchChannel class", t);
        }
    }

    public void setDispatchContext(DispatchContext dispatchContext) {
        this.dispatchContext = dispatchContext;
    }

    public void close() {

    }

    public boolean canDispatch(BIObject document) {
        return canDispatch(dispatchContext, document, dispatchContext.getEmailDispatchDataStore());
    }

    public boolean dispatch(BIObject document, byte[] executionOutput) {
        Map parametersMap;
        String contentType;
        String fileExtension;
        IDataStore emailDispatchDataStore;
        String nameSuffix;
        String descriptionSuffix;
        String containedFileName;
        String zipFileName;
        boolean reportNameInSubject;

        logger.debug("IN");
        try {
            parametersMap = dispatchContext.getParametersMap();
            contentType = dispatchContext.getContentType();
            fileExtension = dispatchContext.getFileExtension();
            emailDispatchDataStore = dispatchContext.getEmailDispatchDataStore();
            nameSuffix = dispatchContext.getNameSuffix();
            descriptionSuffix = dispatchContext.getDescriptionSuffix();
            containedFileName = dispatchContext.getContainedFileName() != null
                    && !dispatchContext.getContainedFileName().equals("") ? dispatchContext.getContainedFileName()
                            : document.getName();
            zipFileName = dispatchContext.getZipMailName() != null && !dispatchContext.getZipMailName().equals("")
                    ? dispatchContext.getZipMailName()
                    : document.getName();
            reportNameInSubject = dispatchContext.isReportNameInSubject();

            String smtphost = SingletonConfig.getInstance().getConfigValue("MAIL.PROFILES.scheduler.smtphost");
            String smtpport = SingletonConfig.getInstance().getConfigValue("MAIL.PROFILES.scheduler.smtpport");
            String smtpssl = SingletonConfig.getInstance().getConfigValue("MAIL.PROFILES.scheduler.useSSL");
            logger.debug(smtphost + " " + smtpport + " use SSL: " + smtpssl);

            //Custom Trusted Store Certificate Options
            String trustedStorePath = SingletonConfig.getInstance()
                    .getConfigValue("MAIL.PROFILES.trustedStore.file");
            String trustedStorePassword = SingletonConfig.getInstance()
                    .getConfigValue("MAIL.PROFILES.trustedStore.password");

            int smptPort = 25;

            if ((smtphost == null) || smtphost.trim().equals(""))
                throw new Exception("Smtp host not configured");
            if ((smtpport == null) || smtpport.trim().equals("")) {
                throw new Exception("Smtp host not configured");
            } else {
                smptPort = Integer.parseInt(smtpport);
            }

            String from = SingletonConfig.getInstance().getConfigValue("MAIL.PROFILES.scheduler.from");
            if ((from == null) || from.trim().equals(""))
                from = "spagobi.scheduler@eng.it";
            String user = SingletonConfig.getInstance().getConfigValue("MAIL.PROFILES.scheduler.user");
            if ((user == null) || user.trim().equals("")) {
                logger.debug("Smtp user not configured");
                user = null;
            }
            //   throw new Exception("Smtp user not configured");
            String pass = SingletonConfig.getInstance().getConfigValue("MAIL.PROFILES.scheduler.password");
            if ((pass == null) || pass.trim().equals("")) {
                logger.debug("Smtp password not configured");
            }
            //   throw new Exception("Smtp password not configured");

            String mailSubj = dispatchContext.getMailSubj();
            mailSubj = StringUtilities.substituteParametersInString(mailSubj, parametersMap, null, false);

            String mailTxt = dispatchContext.getMailTxt();

            String[] recipients = findRecipients(dispatchContext, document, emailDispatchDataStore);
            if (recipients == null || recipients.length == 0) {
                logger.error("No recipients found for email sending!!!");
                return false;
            }

            //Set the host smtp address
            Properties props = new Properties();
            props.put("mail.smtp.host", smtphost);
            props.put("mail.smtp.port", Integer.toString(smptPort));

            // open session
            Session session = null;

            // create autheticator object
            Authenticator auth = null;
            if (user != null) {
                auth = new SMTPAuthenticator(user, pass);
                props.put("mail.smtp.auth", "true");
                //SSL Connection
                if (smtpssl.equals("true")) {
                    Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
                    //props.put("mail.smtp.debug", "true");          
                    props.put("mail.smtps.auth", "true");
                    props.put("mail.smtps.socketFactory.port", Integer.toString(smptPort));
                    if ((!StringUtilities.isEmpty(trustedStorePath))) {
                        /* Dynamic configuration of trustedstore for CA
                         * Using Custom SSLSocketFactory to inject certificates directly from specified files
                         */
                        //System.setProperty("java.security.debug","certpath");
                        //System.setProperty("javax.net.debug","ssl ");
                        props.put("mail.smtps.socketFactory.class", CUSTOM_SSL_FACTORY);

                    } else {
                        //System.setProperty("java.security.debug","certpath");
                        //System.setProperty("javax.net.debug","ssl ");
                        props.put("mail.smtps.socketFactory.class", DEFAULT_SSL_FACTORY);
                    }
                    props.put("mail.smtp.socketFactory.fallback", "false");
                }

                //session = Session.getDefaultInstance(props, auth);
                session = Session.getInstance(props, auth);
                //session.setDebug(true);
                //session.setDebugOut(null);
                logger.info("Session.getInstance(props, auth)");

            } else {
                //session = Session.getDefaultInstance(props);
                session = Session.getInstance(props);
                logger.info("Session.getInstance(props)");
            }

            // create a message
            Message msg = new MimeMessage(session);
            // set the from and to address
            InternetAddress addressFrom = new InternetAddress(from);
            msg.setFrom(addressFrom);
            InternetAddress[] addressTo = new InternetAddress[recipients.length];
            for (int i = 0; i < recipients.length; i++) {
                addressTo[i] = new InternetAddress(recipients[i]);
            }
            msg.setRecipients(Message.RecipientType.TO, addressTo);
            // Setting the Subject and Content Type

            String subject = mailSubj;

            if (reportNameInSubject) {
                subject += " " + document.getName() + nameSuffix;
            }

            msg.setSubject(subject);
            // create and fill the first message part
            MimeBodyPart mbp1 = new MimeBodyPart();
            mbp1.setText(mailTxt + "\n" + descriptionSuffix);
            // create the second message part
            MimeBodyPart mbp2 = new MimeBodyPart();
            // attach the file to the message

            SchedulerDataSource sds = null;
            //if zip requested
            if (dispatchContext.isZipMailDocument()) {
                mbp2 = zipAttachment(executionOutput, containedFileName, zipFileName, nameSuffix, fileExtension);
            }
            //else 
            else {
                sds = new SchedulerDataSource(executionOutput, contentType,
                        containedFileName + nameSuffix + fileExtension);
                mbp2.setDataHandler(new DataHandler(sds));
                mbp2.setFileName(sds.getName());
            }

            // create the Multipart and add its parts to it
            Multipart mp = new MimeMultipart();
            mp.addBodyPart(mbp1);
            mp.addBodyPart(mbp2);
            // add the Multipart to the message
            msg.setContent(mp);
            // send message
            if ((smtpssl.equals("true")) && (!StringUtilities.isEmpty(user)) && (!StringUtilities.isEmpty(pass))) {
                //USE SSL Transport comunication with SMTPS
                Transport transport = session.getTransport("smtps");
                transport.connect(smtphost, smptPort, user, pass);
                transport.sendMessage(msg, msg.getAllRecipients());
                transport.close();
            } else {
                //Use normal SMTP
                Transport.send(msg);
            }
        } catch (Exception e) {
            logger.error("Error while sending schedule result mail", e);
            return false;
        } finally {
            logger.debug("OUT");
        }
        return true;
    }

    private MimeBodyPart zipAttachment(byte[] attach, String containedFileName, String zipFileName,
            String nameSuffix, String fileExtension) {
        MimeBodyPart messageBodyPart = null;
        try {

            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            ZipOutputStream zipOut = new ZipOutputStream(bout);
            String entryName = containedFileName + nameSuffix + fileExtension;
            zipOut.putNextEntry(new ZipEntry(entryName));
            zipOut.write(attach);
            zipOut.closeEntry();

            zipOut.close();

            messageBodyPart = new MimeBodyPart();
            DataSource source = new ByteArrayDataSource(bout.toByteArray(), "application/zip");
            messageBodyPart.setDataHandler(new DataHandler(source));
            messageBodyPart.setFileName(zipFileName + nameSuffix + ".zip");

        } catch (Exception e) {
            // TODO: handle exception            
        }
        return messageBodyPart;
    }

    private byte[] zipDocument(String fileZipName, byte[] content) {
        logger.debug("IN");

        ByteArrayOutputStream bos = null;
        ZipOutputStream zos = null;
        ByteArrayInputStream in = null;
        try {

            bos = new ByteArrayOutputStream();
            zos = new ZipOutputStream(bos);
            ZipEntry ze = new ZipEntry(fileZipName);
            zos.putNextEntry(ze);
            in = new ByteArrayInputStream(content);

            for (int c = in.read(); c != -1; c = in.read()) {
                zos.write(c);
            }

            return bos.toByteArray();

        } catch (IOException ex) {
            logger.error("Error zipping the document", ex);
            return null;
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    logger.error("Error closing output stream", e);
                }
            }
            if (zos != null) {
                try {
                    zos.close();
                } catch (IOException e) {
                    logger.error("Error closing output stream", e);
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    logger.error("Error closing output stream", e);
                }
            }
        }

    }

    public static boolean canDispatch(DispatchContext dispatchContext, BIObject document,
            IDataStore emailDispatchDataStore) {
        String[] recipients = findRecipients(dispatchContext, document, emailDispatchDataStore);
        return (recipients != null && recipients.length > 0);
    }

    private static String[] findRecipients(DispatchContext info, BIObject biobj, IDataStore dataStore) {
        logger.debug("IN");
        String[] toReturn = null;
        List<String> recipients = new ArrayList();
        try {
            recipients.addAll(findRecipientsFromFixedList(info));
        } catch (Exception e) {
            logger.error(e);
        }
        try {
            recipients.addAll(findRecipientsFromDataSet(info, biobj, dataStore));
        } catch (Exception e) {
            logger.error(e);
        }
        try {
            recipients.addAll(findRecipientsFromExpression(info, biobj));
        } catch (Exception e) {
            logger.error(e);
        }
        // validates addresses
        List<String> validRecipients = new ArrayList();
        Iterator it = recipients.iterator();
        while (it.hasNext()) {
            String recipient = (String) it.next();
            if (GenericValidator.isBlankOrNull(recipient) || !GenericValidator.isEmail(recipient)) {
                logger.error("[" + recipient + "] is not a valid email address.");
                continue;
            }
            if (validRecipients.contains(recipient))
                continue;
            validRecipients.add(recipient);
        }
        toReturn = validRecipients.toArray(new String[0]);
        logger.debug("OUT: returning " + toReturn);
        return toReturn;
    }

    private static List<String> findRecipientsFromFixedList(DispatchContext info) throws Exception {
        logger.debug("IN");
        List<String> recipients = new ArrayList();
        if (info.isUseFixedRecipients()) {
            logger.debug("Trigger is configured to send mail to fixed recipients: " + info.getMailTos());
            if (info.getMailTos() == null || info.getMailTos().trim().equals("")) {
                throw new Exception("Missing fixed recipients list!!!");
            }
            // in this case recipients are fixed and separated by ","
            String[] fixedRecipients = info.getMailTos().split(",");
            logger.debug("Fixed recipients found: " + fixedRecipients);
            recipients.addAll(Arrays.asList(fixedRecipients));
        }
        logger.debug("OUT");
        return recipients;
    }

    private static List<String> findRecipientsFromExpression(DispatchContext info, BIObject biobj)
            throws Exception {
        logger.debug("IN");
        List<String> recipients = new ArrayList();
        if (info.isUseExpression()) {
            logger.debug("Trigger is configured to send mail using an expression: " + info.getExpression());
            String expression = info.getExpression();
            if (expression == null || expression.trim().equals("")) {
                throw new Exception("Missing recipients expression!!!");
            }
            // building a map for parameters value substitution
            Map parametersMap = new HashMap();
            List parameters = biobj.getBiObjectParameters();
            Iterator it = parameters.iterator();
            while (it.hasNext()) {
                BIObjectParameter parameter = (BIObjectParameter) it.next();
                List values = parameter.getParameterValues();
                if (values != null && !values.isEmpty()) {
                    parametersMap.put(parameter.getLabel(), values.get(0));
                } else {
                    parametersMap.put(parameter.getLabel(), "");
                }
            }
            // we must substitute parameter values on the expression
            String recipientStr = StringUtilities.substituteParametersInString(expression, parametersMap, null,
                    false);
            logger.debug("The expression, after substitution, now is [" + recipientStr + "].");
            String[] recipientsArray = recipientStr.split(",");
            logger.debug("Recipients found with expression: " + recipientsArray);
            recipients.addAll(Arrays.asList(recipientsArray));
        }
        logger.debug("OUT");
        return recipients;
    }

    private static List<String> findRecipientsFromDataSet(DispatchContext info, BIObject biobj,
            IDataStore dataStore) throws Exception {
        logger.debug("IN");
        List<String> recipients = new ArrayList();
        if (info.isUseDataSet()) {
            logger.debug("Trigger is configured to send mail to recipients retrieved by a dataset");
            if (dataStore == null || dataStore.isEmpty()) {
                throw new Exception("The dataset in input is empty!! Cannot retrieve recipients from it.");
            }
            // in this case recipients must be retrieved by the dataset (which the datastore in input belongs to)
            // we must find the parameter value in order to filter the dataset
            String dsParameterLabel = info.getDataSetParameterLabel();
            logger.debug("The dataset will be filtered using the value of the parameter " + dsParameterLabel);
            // looking for the parameter
            List parameters = biobj.getBiObjectParameters();
            BIObjectParameter parameter = null;
            String codeValue = null;
            Iterator parameterIt = parameters.iterator();
            while (parameterIt.hasNext()) {
                BIObjectParameter aParameter = (BIObjectParameter) parameterIt.next();
                if (aParameter.getLabel().equalsIgnoreCase(dsParameterLabel)) {
                    parameter = aParameter;
                    break;
                }
            }
            if (parameter == null) {
                throw new Exception("The document parameter with label [" + dsParameterLabel
                        + "] was not found. Cannot filter the dataset.");
            }

            // considering the first value of the parameter
            List values = parameter.getParameterValues();
            if (values == null || values.isEmpty()) {
                throw new Exception("The document parameter with label [" + dsParameterLabel
                        + "] has no values. Cannot filter the dataset.");
            }

            codeValue = (String) values.get(0);
            logger.debug("Using value [" + codeValue + "] for dataset filtering...");

            Iterator it = dataStore.iterator();
            while (it.hasNext()) {
                String recipient = null;
                IRecord record = (IRecord) it.next();
                // the parameter value is used to filter on the first dataset field
                IField valueField = (IField) record.getFieldAt(0);
                Object valueObj = valueField.getValue();
                String value = null;
                if (valueObj != null)
                    value = valueObj.toString();
                if (codeValue.equals(value)) {
                    logger.debug("Found value [" + codeValue + "] on the first field of a record of the dataset.");
                    // recipient address is on the second dataset field
                    IField recipientField = (IField) record.getFieldAt(1);
                    Object recipientFieldObj = recipientField.getValue();
                    if (recipientFieldObj != null) {
                        recipient = recipientFieldObj.toString();
                        // in this case recipients can be separated by ","
                        String[] multiRecipients = recipient.split(",");

                        recipients.addAll(Arrays.asList(multiRecipients));

                        logger.debug("DataSet multi recipients found: " + Arrays.deepToString(multiRecipients));
                    } else {
                        logger.warn("The second field of the record is null.");
                    }
                }
                if (recipient != null) {
                    recipients.add(recipient);
                }
            }
            logger.debug("Recipients found from dataset: " + recipients.toArray());
        }
        logger.debug("OUT");
        return recipients;
    }

    private class SMTPAuthenticator extends javax.mail.Authenticator {
        private String username = "";
        private String password = "";

        public PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(username, password);
        }

        public SMTPAuthenticator(String user, String pass) {
            this.username = user;
            this.password = pass;
        }
    }

    private class SchedulerDataSource implements DataSource {

        byte[] content = null;
        String name = null;
        String contentType = null;

        public String getContentType() {
            return contentType;
        }

        public InputStream getInputStream() throws IOException {
            ByteArrayInputStream bais = new ByteArrayInputStream(content);
            return bais;
        }

        public String getName() {
            return name;
        }

        public OutputStream getOutputStream() throws IOException {
            return null;
        }

        public SchedulerDataSource(byte[] content, String contentType, String name) {
            this.content = content;
            this.contentType = contentType;
            this.name = name;
        }
    }

}