MailActivityBehavior.java 14.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.activiti5.engine.impl.bpmn.behavior;

import java.io.File;
17 18 19 20 21
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
22 23 24 25

import javax.activation.DataSource;
import javax.naming.NamingException;

26
import org.activiti.engine.cfg.MailServerInfo;
27 28
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.Expression;
29 30 31 32 33
import org.activiti5.engine.ActivitiException;
import org.activiti5.engine.ActivitiIllegalArgumentException;
import org.activiti5.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti5.engine.impl.context.Context;
import org.activiti5.engine.impl.pvm.delegate.ActivityExecution;
34 35 36 37 38
import org.apache.commons.mail.Email;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import org.apache.commons.mail.MultiPartEmail;
import org.apache.commons.mail.SimpleEmail;
39 40 41 42 43 44 45 46 47 48
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Joram Barrez
 * @author Frederik Heremans
 * @author Tim Stephenson
 */
public class MailActivityBehavior extends AbstractBpmnActivityBehavior {

49 50 51 52
  private static final long serialVersionUID = 1L;

  private static final Logger LOG = LoggerFactory.getLogger(MailActivityBehavior.class);

53 54 55
  private static final Class<?>[] ALLOWED_ATT_TYPES = new Class<?>[]{
      File.class, File[].class, String.class, String[].class, DataSource.class, DataSource[].class
  };
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71

  protected Expression to;
  protected Expression from;
  protected Expression cc;
  protected Expression bcc;
  protected Expression subject;
  protected Expression text;
  protected Expression textVar;
  protected Expression html;
  protected Expression htmlVar;
  protected Expression charset;
  protected Expression ignoreException;
  protected Expression exceptionVariableName;
  protected Expression attachments;

  @Override
72
  public void execute(DelegateExecution execution) {
73 74 75 76 77 78 79 80 81 82

    boolean doIgnoreException = Boolean.parseBoolean(getStringFromField(ignoreException, execution));
    String exceptionVariable = getStringFromField(exceptionVariableName, execution);
    Email email = null;
    try {
      String toStr = getStringFromField(to, execution);
      String fromStr = getStringFromField(from, execution);
      String ccStr = getStringFromField(cc, execution);
      String bccStr = getStringFromField(bcc, execution);
      String subjectStr = getStringFromField(subject, execution);
83 84 85 86
      String textStr = textVar == null ? getStringFromField(text, execution)
          : getStringFromField(getExpression(execution, textVar), execution);
      String htmlStr = htmlVar == null ? getStringFromField(html, execution)
          : getStringFromField(getExpression(execution, htmlVar), execution);
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
      String charSetStr = getStringFromField(charset, execution);
      List<File> files = new LinkedList<File>();
      List<DataSource> dataSources = new LinkedList<DataSource>();
      getFilesFromFields(attachments, execution, files, dataSources);

      email = createEmail(textStr, htmlStr, attachmentsExist(files, dataSources));
      addTo(email, toStr);
      setFrom(email, fromStr, execution.getTenantId());
      addCc(email, ccStr);
      addBcc(email, bccStr);
      setSubject(email, subjectStr);
      setMailServerProperties(email, execution.getTenantId());
      setCharset(email, charSetStr);
      attach(email, files, dataSources);

      email.send();

    } catch (ActivitiException e) {
      handleException(execution, e.getMessage(), e, doIgnoreException, exceptionVariable);
    } catch (EmailException e) {
      handleException(execution, "Could not send e-mail in execution " + execution.getId(), e, doIgnoreException, exceptionVariable);
108
    }
T
Tijs Rademakers 已提交
109

110
    leave((ActivityExecution) execution);
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
  }

  private boolean attachmentsExist(List<File> files, List<DataSource> dataSources) {
    return !((files == null || files.isEmpty()) && (dataSources == null || dataSources.isEmpty()));
  }

  protected Email createEmail(String text, String html, boolean attachmentsExist) {
    if (html != null) {
      return createHtmlEmail(text, html);
    } else if (text != null) {
      if (!attachmentsExist) {
        return createTextOnlyEmail(text);
      } else {
        return createMultiPartEmail(text);
      }
    } else {
      throw new ActivitiIllegalArgumentException("'html' or 'text' is required to be defined when using the mail activity");
128
    }
129 130 131 132 133 134 135 136 137 138 139 140
  }

  protected HtmlEmail createHtmlEmail(String text, String html) {
    HtmlEmail email = new HtmlEmail();
    try {
      email.setHtmlMsg(html);
      if (text != null) { // for email clients that don't support html
        email.setTextMsg(text);
      }
      return email;
    } catch (EmailException e) {
      throw new ActivitiException("Could not create HTML email", e);
141
    }
142 143 144 145 146 147 148 149 150
  }

  protected SimpleEmail createTextOnlyEmail(String text) {
    SimpleEmail email = new SimpleEmail();
    try {
      email.setMsg(text);
      return email;
    } catch (EmailException e) {
      throw new ActivitiException("Could not create text-only email", e);
151
    }
152 153 154 155 156 157 158 159 160
  }

  protected MultiPartEmail createMultiPartEmail(String text) {
    MultiPartEmail email = new MultiPartEmail();
    try {
      email.setMsg(text);
      return email;
    } catch (EmailException e) {
      throw new ActivitiException("Could not create text-only email", e);
161
    }
162
  }
163

164 165 166 167
  protected void addTo(Email email, String to) {
    String[] tos = splitAndTrim(to);
    if (tos != null) {
      for (String t : tos) {
168
        try {
169
          email.addTo(t);
170
        } catch (EmailException e) {
171
          throw new ActivitiException("Could not add " + t + " as recipient", e);
T
Tijs Rademakers 已提交
172
        }
173 174 175
      }
    } else {
      throw new ActivitiException("No recipient could be found for sending email");
T
Tijs Rademakers 已提交
176
    }
177 178 179 180 181 182 183 184 185 186 187 188 189
  }

  protected void setFrom(Email email, String from, String tenantId) {
    String fromAddress = null;

    if (from != null) {
      fromAddress = from;
    } else { // use default configured from address in process engine config
      if (tenantId != null && tenantId.length() > 0) {
        Map<String, MailServerInfo> mailServers = Context.getProcessEngineConfiguration().getMailServers();
        if (mailServers != null && mailServers.containsKey(tenantId)) {
          MailServerInfo mailServerInfo = mailServers.get(tenantId);
          fromAddress = mailServerInfo.getMailServerDefaultFrom();
190
        }
191 192 193 194 195
      }

      if (fromAddress == null) {
        fromAddress = Context.getProcessEngineConfiguration().getMailServerDefaultFrom();
      }
196 197
    }

198 199 200 201 202 203
    try {
      email.setFrom(fromAddress);
    } catch (EmailException e) {
      throw new ActivitiException("Could not set " + from + " as from address in email", e);
    }
  }
T
Tijs Rademakers 已提交
204

205 206 207 208
  protected void addCc(Email email, String cc) {
    String[] ccs = splitAndTrim(cc);
    if (ccs != null) {
      for (String c : ccs) {
209
        try {
210
          email.addCc(c);
211
        } catch (EmailException e) {
212
          throw new ActivitiException("Could not add " + c + " as cc recipient", e);
213
        }
214
      }
215
    }
216
  }
217

218 219 220 221 222 223 224 225
  protected void addBcc(Email email, String bcc) {
    String[] bccs = splitAndTrim(bcc);
    if (bccs != null) {
      for (String b : bccs) {
        try {
          email.addBcc(b);
        } catch (EmailException e) {
          throw new ActivitiException("Could not add " + b + " as bcc recipient", e);
T
Tijs Rademakers 已提交
226
        }
227
      }
228
    }
229
  }
T
Tijs Rademakers 已提交
230

231 232 233
  protected void attach(Email email, List<File> files, List<DataSource> dataSources) throws EmailException {
    if (!(email instanceof MultiPartEmail && attachmentsExist(files, dataSources))) {
      return;
234
    }
235 236 237
    MultiPartEmail mpEmail = (MultiPartEmail) email;
    for (File file : files) {
      mpEmail.attach(file);
T
Tijs Rademakers 已提交
238
    }
239 240 241 242
    for (DataSource ds : dataSources) {
      if (ds != null) {
        mpEmail.attach(ds, ds.getName(), null);
      }
T
Tijs Rademakers 已提交
243
    }
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
  }

  protected void setSubject(Email email, String subject) {
    email.setSubject(subject != null ? subject : "");
  }

  protected void setMailServerProperties(Email email, String tenantId) {
    ProcessEngineConfigurationImpl processEngineConfiguration = Context.getProcessEngineConfiguration();

    boolean isMailServerSet = false;
    if (tenantId != null && tenantId.length() > 0) {
      if (processEngineConfiguration.getMailSessionJndi(tenantId) != null) {
        setEmailSession(email, processEngineConfiguration.getMailSessionJndi(tenantId));
        isMailServerSet = true;

      } else if (processEngineConfiguration.getMailServer(tenantId) != null) {
        MailServerInfo mailServerInfo = processEngineConfiguration.getMailServer(tenantId);
        String host = mailServerInfo.getMailServerHost();
        if (host == null) {
          throw new ActivitiException("Could not send email: no SMTP host is configured for tenantId " + tenantId);
        }
        email.setHostName(host);
266

267
        email.setSmtpPort(mailServerInfo.getMailServerPort());
268

269 270
        email.setSSLOnConnect(processEngineConfiguration.getMailServerUseSSL());
        email.setStartTLSEnabled(processEngineConfiguration.getMailServerUseTLS());
271

272 273 274 275 276
        String user = mailServerInfo.getMailServerUsername();
        String password = mailServerInfo.getMailServerPassword();
        if (user != null && password != null) {
          email.setAuthentication(user, password);
        }
277

278 279 280
        isMailServerSet = true;
      }
    }
281

282 283 284 285
    if (!isMailServerSet) {
      String mailSessionJndi = processEngineConfiguration.getMailSessionJndi();
      if (mailSessionJndi != null) {
        setEmailSession(email, mailSessionJndi);
286

287 288 289 290
      } else {
        String host = processEngineConfiguration.getMailServerHost();
        if (host == null) {
          throw new ActivitiException("Could not send email: no SMTP host is configured");
T
Tijs Rademakers 已提交
291
        }
292
        email.setHostName(host);
293

294 295
        int port = processEngineConfiguration.getMailServerPort();
        email.setSmtpPort(port);
296

297 298
        email.setSSLOnConnect(processEngineConfiguration.getMailServerUseSSL());
        email.setStartTLSEnabled(processEngineConfiguration.getMailServerUseTLS());
299

300 301 302 303
        String user = processEngineConfiguration.getMailServerUsername();
        String password = processEngineConfiguration.getMailServerPassword();
        if (user != null && password != null) {
          email.setAuthentication(user, password);
T
Tijs Rademakers 已提交
304
        }
305
      }
306
    }
307
  }
T
Tijs Rademakers 已提交
308

309 310 311 312 313
  protected void setEmailSession(Email email, String mailSessionJndi) {
    try {
      email.setMailSessionFromJNDI(mailSessionJndi);
    } catch (NamingException e) {
      throw new ActivitiException("Could not send email: Incorrect JNDI configuration", e);
314
    }
315
  }
T
Tijs Rademakers 已提交
316

317 318 319
  protected void setCharset(Email email, String charSetStr) {
    if (charset != null) {
      email.setCharset(charSetStr);
320
    }
321 322 323 324 325 326 327 328 329
  }

  protected String[] splitAndTrim(String str) {
    if (str != null) {
      String[] splittedStrings = str.split(",");
      for (int i = 0; i < splittedStrings.length; i++) {
        splittedStrings[i] = splittedStrings[i].trim();
      }
      return splittedStrings;
330
    }
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
    return null;
  }

  protected String getStringFromField(Expression expression, DelegateExecution execution) {
    if (expression != null) {
      Object value = expression.getValue(execution);
      if (value != null) {
        return value.toString();
      }
    }
    return null;
  }

  private void getFilesFromFields(Expression expression, DelegateExecution execution, List<File> files, List<DataSource> dataSources) {
    Object value = checkAllowedTypes(expression, execution);
    if (value != null) {
      if (value instanceof File) {
        files.add((File) value);
      } else if (value instanceof String) {
        files.add(new File((String) value));
      } else if (value instanceof File[]) {
        Collections.addAll(files, (File[]) value);
      } else if (value instanceof String[]) {
        String[] paths = (String[]) value;
        for (String path : paths) {
          files.add(new File(path));
T
Tijs Rademakers 已提交
357
        }
358 359 360 361 362 363 364
      } else if (value instanceof DataSource) {
        dataSources.add((DataSource) value);
      } else if (value instanceof DataSource[]) {
        for (DataSource ds : (DataSource[]) value) {
          if (ds != null) {
            dataSources.add(ds);
          }
T
Tijs Rademakers 已提交
365
        }
366
      }
367
    }
368
    for (Iterator<File> it = files.iterator(); it.hasNext(); ) {
369 370 371 372
      File file = it.next();
      if (!fileExists(file)) {
        it.remove();
      }
373
    }
374
  }
T
Tijs Rademakers 已提交
375

376 377 378
  private Object checkAllowedTypes(Expression expression, DelegateExecution execution) {
    if (expression == null) {
      return null;
379
    }
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
    Object value = expression.getValue(execution);
    if (value == null) {
      return null;
    }
    for (Class<?> allowedType : ALLOWED_ATT_TYPES) {
      if (allowedType.isInstance(value)) {
        return value;
      }
    }
    throw new ActivitiException("Invalid attachment type: " + value.getClass());
  }

  protected boolean fileExists(File file) {
    return file != null && file.exists() && file.isFile() && file.canRead();
  }

396
  protected Expression getExpression(DelegateExecution execution, Expression var) {
397 398 399 400
    String variable = (String) execution.getVariable(var.getExpressionText());
    return Context.getProcessEngineConfiguration().getExpressionManager().createExpression(variable);
  }

401
  protected void handleException(DelegateExecution execution, String msg, Exception e, boolean doIgnoreException, String exceptionVariable) {
402 403 404 405 406 407 408 409 410 411 412
    if (doIgnoreException) {
      LOG.info("Ignoring email send error: " + msg, e);
      if (exceptionVariable != null && exceptionVariable.length() > 0) {
        execution.setVariable(exceptionVariable, msg);
      }
    } else {
      if (e instanceof ActivitiException) {
        throw (ActivitiException) e;
      } else {
        throw new ActivitiException(msg, e);
      }
413
    }
414
  }
415
}