public void executeEmailNotification(
      EmailNotification notification, Task task, EntityManager em) {
    Map<String, EmailNotificationHeader> headers = notification.getEmailHeaders();

    // group users into languages
    Map<String, List<User>> users = new HashMap<String, List<User>>();
    for (OrganizationalEntity entity : notification.getBusinessAdministrators()) {
      if (entity instanceof Group) {
        buildMapByLanguage(users, (Group) entity);
      } else {
        buildMapByLanguage(users, (User) entity);
      }
    }

    for (OrganizationalEntity entity : notification.getRecipients()) {
      if (entity instanceof Group) {
        buildMapByLanguage(users, (Group) entity);
      } else {
        buildMapByLanguage(users, (User) entity);
      }
    }

    TaskData taskData = task.getTaskData();
    Map<String, Object> doc = null;
    if (taskData != null) {
      Content content = em.find(Content.class, taskData.getDocumentContentId());
      if (content != null) {
        ExpressionCompiler compiler = new ExpressionCompiler(new String(content.getContent()));
        doc = (Map<String, Object>) MVEL.executeExpression(compiler.compile());
      } else {
        doc = Collections.emptyMap();
      }
    }

    for (Iterator<Entry<String, List<User>>> it = users.entrySet().iterator(); it.hasNext(); ) {
      Entry<String, List<User>> entry = it.next();
      EmailNotificationHeader header = headers.get(entry.getKey());

      Map<String, Object> email = new HashMap<String, Object>();
      StringBuilder to = new StringBuilder();
      boolean first = true;
      for (User user : entry.getValue()) {
        if (!first) {
          to.append(';');
        }
        String emailAddress = userInfo.getEmailForEntity(user);
        to.append(emailAddress);
        first = false;
      }
      email.put("To", to.toString());

      if (header.getFrom() != null && header.getFrom().trim().length() > 0) {
        email.put("From", header.getFrom());
      } else {
        email.put("From", from);
      }

      if (header.getReplyTo() != null && header.getReplyTo().trim().length() > 0) {
        email.put("Reply-To", header.getReplyTo());
      } else {
        email.put("Reply-To", replyTo);
      }

      Map<String, Object> vars = new HashMap<String, Object>();
      vars.put("doc", doc);
      String subject = (String) TemplateRuntime.eval(header.getSubject(), vars);
      String body = (String) TemplateRuntime.eval(header.getBody(), vars);

      email.put("Subject", subject);
      email.put("Body", body);

      WorkItemImpl workItem = new WorkItemImpl();
      workItem.setParameters(email);

      handler.executeWorkItem(workItem, manager);
    }
  }