NotificationServiceImpl.java

  1. /*
  2.  * This software was designed and created by Jason Carroll.
  3.  * Copyright (c) 2002, 2003, 2004 Jason Carroll.
  4.  * The author can be reached at jcarroll@cowsultants.com
  5.  * ITracker website: http://www.cowsultants.com
  6.  * ITracker forums: http://www.cowsultants.com/phpBB/index.php
  7.  *
  8.  * This program is free software; you can redistribute it and/or modify
  9.  * it only under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; either version 2 of the License, or
  11.  * (at your option) any later version.
  12.  *
  13.  * This program is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  */

  18. package org.itracker.services.implementations;

  19. import org.apache.commons.lang.StringUtils;
  20. import org.apache.log4j.Logger;
  21. import org.itracker.core.resources.ITrackerResources;
  22. import org.itracker.model.*;
  23. import org.itracker.model.Notification.Role;
  24. import org.itracker.model.Notification.Type;
  25. import org.itracker.model.util.IssueUtilities;
  26. import org.itracker.model.util.ProjectUtilities;
  27. import org.itracker.model.util.UserUtilities;
  28. import org.itracker.persistence.dao.IssueActivityDAO;
  29. import org.itracker.persistence.dao.IssueDAO;
  30. import org.itracker.persistence.dao.NotificationDAO;
  31. import org.itracker.services.EmailService;
  32. import org.itracker.services.IssueService;
  33. import org.itracker.services.NotificationService;
  34. import org.itracker.services.ProjectService;
  35. import org.itracker.util.HTMLUtilities;
  36. import org.springframework.beans.BeansException;
  37. import org.springframework.context.ApplicationContext;
  38. import org.springframework.context.ApplicationContextAware;

  39. import javax.mail.internet.InternetAddress;
  40. import java.net.MalformedURLException;
  41. import java.util.*;

  42. public class NotificationServiceImpl implements NotificationService, ApplicationContextAware {
  43.     public static final Integer DEFAULT_ISSUE_AGE = 30;


  44.     private EmailService emailService;
  45.     private NotificationDAO notificationDao;
  46.     private ProjectService projectService;
  47.     private IssueActivityDAO issueActivityDao;
  48.     private IssueDAO issueDao;


  49.     private String issueServiceName;

  50.     private static final Logger logger = Logger
  51.             .getLogger(NotificationServiceImpl.class);
  52.     private IssueService issueService;
  53.     private ApplicationContext applicationContext;

  54.     public NotificationServiceImpl() {

  55.         this.emailService = null;
  56.         this.projectService = null;
  57.         this.notificationDao = null;
  58.     }

  59.     @Override
  60.     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  61.         this.applicationContext = applicationContext;
  62.     }

  63.     public NotificationServiceImpl(EmailService emailService,
  64.                                    ProjectService projectService, NotificationDAO notificationDao, IssueActivityDAO issueActivityDao, IssueDAO issueDao, IssueService issueService) {
  65.         this();
  66.         this.setEmailService(emailService);
  67.         this.setProjectService(projectService);
  68.         this.setNotificationDao(notificationDao);
  69.         this.setIssueActivityDao(issueActivityDao);
  70.         this.setIssueDao(issueDao);
  71.         this.setIssueService(issueService);
  72.     }

  73.     public void sendNotification(Notification notification, Type type,
  74.                                  String url) {

  75.         if (logger.isDebugEnabled()) {
  76.             logger.debug("sendNotification: called with notification: "
  77.                     + notification + ", type: " + url + ", url: " + url);
  78.         }
  79.         if (null == notification) {
  80.             throw new IllegalArgumentException("notification must not be null");
  81.         }
  82.         if (null == this.emailService || null == this.notificationDao) {
  83.             throw new IllegalStateException("service not initialized yet");
  84.         }
  85.         if (type == Type.SELF_REGISTER) {
  86.             this.handleSelfRegistrationNotification(notification.getUser()
  87.                     .getLogin(), notification.getUser().getEmailAddress(), notification.getUser().getPreferences().getUserLocale(), url);
  88.         } else {
  89.             handleIssueNotification(notification.getIssue(), type, url);

  90.         }

  91.     }

  92.     public void sendNotification(Issue issue, Type type, String baseURL) {
  93.         if (logger.isDebugEnabled()) {
  94.             logger.debug("sendNotification: called with issue: " + issue
  95.                     + ", type: " + type + ", baseURL: " + baseURL);
  96.         }
  97.         handleIssueNotification(issue, type, baseURL);

  98.     }

  99.     public void setEmailService(EmailService emailService) {

  100.         if (null == emailService)
  101.             throw new IllegalArgumentException("email service must not be null");

  102.         if (null != this.emailService) {
  103.             throw new IllegalStateException("email service allready set");
  104.         }
  105.         this.emailService = emailService;

  106.     }


  107.     private void handleSelfRegistrationNotification(String login,
  108.                                                     InternetAddress toAddress, String locale, String url) {
  109.         if (logger.isDebugEnabled()) {
  110.             logger
  111.                     .debug("handleSelfRegistrationNotification: called with login: "
  112.                             + login
  113.                             + ", toAddress"
  114.                             + toAddress
  115.                             + ", url: "
  116.                             + url);
  117.         }
  118.         try {

  119.             if (toAddress != null && !"".equals(toAddress.getAddress())) {
  120.                 String subject = ITrackerResources
  121.                         .getString("itracker.email.selfreg.subject", locale);
  122.                 String msgText = ITrackerResources.getString(
  123.                         "itracker.email.selfreg.body", locale, new Object[]{login,
  124.                                 url + "/login.do"});
  125.                 emailService.sendEmail(toAddress, subject, msgText);
  126.             } else {
  127.                 throw new IllegalArgumentException(
  128.                         "To-address must be set for self registration notification.");
  129.             }
  130.         } catch (RuntimeException e) {
  131.             logger.error("failed to handle self registration notification for "
  132.                     + toAddress, e);
  133.             throw e;
  134.         }
  135.     }

  136.     /**
  137.      * Method for internal sending of a notification of specific type.
  138.      */
  139.     private void handleIssueNotification(Issue issue, Type type, String url) {

  140.         if (logger.isDebugEnabled()) {
  141.             logger.debug("handleIssueNotification: called with issue: " + issue
  142.                     + ", type: " + type + "url: " + url);
  143.         }
  144.         this.handleLocalizedIssueNotification(issue, type, url, null, null);
  145.     }


  146.     /**
  147.      * Method for internal sending of a notification of specific type.
  148.      */
  149.     private void handleLocalizedIssueNotification(final Issue issue, final Type type, final String url,
  150.                                                   final InternetAddress[] recipients, Integer lastModifiedDays) {
  151.         try {

  152.             if (logger.isDebugEnabled()) {
  153.                 logger
  154.                         .debug("handleLocalizedIssueNotification: running as thread, called with issue: "
  155.                                 + issue
  156.                                 + ", type: "
  157.                                 + type
  158.                                 + "url: "
  159.                                 + url
  160.                                 + ", recipients: "
  161.                                 + (null == recipients ? "<null>" : String
  162.                                 .valueOf(Arrays.asList(recipients)))
  163.                                 + ", lastModifiedDays: " + lastModifiedDays);
  164.             }

  165.             final Integer notModifiedSince;

  166.             if (lastModifiedDays == null || lastModifiedDays < 0) {
  167.                 notModifiedSince = DEFAULT_ISSUE_AGE;
  168.             } else {
  169.                 notModifiedSince = lastModifiedDays;
  170.             }

  171.             try {
  172.                 if (logger.isDebugEnabled()) {
  173.                     logger
  174.                             .debug("handleLocalizedIssueNotification.run: running as thread, called with issue: "
  175.                                     + issue
  176.                                     + ", type: "
  177.                                     + type
  178.                                     + "url: "
  179.                                     + url
  180.                                     + ", recipients: "
  181.                                     + (null == recipients ? "<null>" : String
  182.                                     .valueOf(Arrays.asList(recipients)))
  183.                                     + ", notModifiedSince: " + notModifiedSince);
  184.                 }
  185.                 final List<Notification> notifications;
  186.                 if (issue == null) {
  187.                     logger
  188.                             .warn("handleLocalizedIssueNotification: issue was null. Notification will not be handled");
  189.                     return;
  190.                 }
  191.                 Map<InternetAddress, Locale> localeMapping;

  192.                 if (recipients == null) {
  193.                     notifications = this.getIssueNotifications(issue);

  194.                     localeMapping = new Hashtable<>(notifications.size());
  195.                     Iterator<Notification> it = notifications.iterator();
  196.                     User currentUser;
  197.                     while (it.hasNext()) {
  198.                         currentUser = it.next().getUser();
  199.                         if (null != currentUser
  200.                                 && null != currentUser.getEmailAddress()
  201.                                 && null != currentUser.getEmail()
  202.                                 && (!localeMapping.keySet()
  203.                                 .contains(currentUser.getEmailAddress()))) {

  204.                             try {
  205.                                 localeMapping.put(currentUser.getEmailAddress(), ITrackerResources.getLocale(currentUser.getPreferences().getUserLocale()));
  206.                             } catch (RuntimeException re) {
  207.                                 localeMapping.put(currentUser.getEmailAddress(), ITrackerResources.getLocale());
  208.                             }
  209.                         }
  210.                     }
  211.                 } else {
  212.                     localeMapping = new Hashtable<>(1);
  213.                     Locale locale = ITrackerResources.getLocale();
  214.                     for (InternetAddress internetAddress : Arrays.asList(recipients)) {
  215.                         localeMapping.put(internetAddress, locale);
  216.                     }
  217.                 }

  218.                 this.handleNotification(issue, type, localeMapping, url, notModifiedSince);
  219.             } catch (Exception e) {
  220.                 logger.error("run: failed to process notification", e);
  221.             }

  222.         } catch (Exception e) {
  223.             logger
  224.                     .error(
  225.                             "handleLocalizedIssueNotification: unexpected exception caught, throwing runtime exception",
  226.                             e);
  227.             throw new RuntimeException(e);
  228.         }
  229.     }

  230.     @Override
  231.     public void sendReminder(Issue issue, User user, String baseURL, int issueAge) {
  232.         Map<InternetAddress, Locale> recipient = new HashMap<>(1);
  233.         recipient.put(user.getEmailAddress(), ITrackerResources.getLocale(user.getPreferences().getUserLocale()));
  234.         handleNotification(issue, Type.ISSUE_REMINDER, recipient, baseURL, issueAge);
  235.     }

  236.     /**
  237.      * Send notifications to mapped addresses by locale.
  238.      */
  239.     private void handleNotification(Issue issue, Type type, Map<InternetAddress, Locale> recipientsLocales, final String url, Integer notModifiedSince) {
  240.         Set<InternetAddress> recipients;
  241.         Map<Locale, Set<InternetAddress>> localeRecipients = new Hashtable<>();

  242.         List<Component> components = issue.getComponents();

  243.         List<IssueActivity> activity = getIssueService().getIssueActivity(
  244.                 issue.getId(), false);

  245.         IssueHistory history;
  246.         history = getIssueService().getLastIssueHistory(issue.getId());
  247.         StringBuilder recipientsString = new StringBuilder();

  248.         if (logger.isDebugEnabled() && null != history) {
  249.             logger.debug("handleIssueNotification: got most recent history: "
  250.                     + history
  251.                     + " ("
  252.                     + history.getDescription()
  253.                     + ")");
  254.         }

  255.         for (InternetAddress internetAddress : recipientsLocales.keySet()) {
  256.             recipientsString.append("\n  ");
  257.             recipientsString.append(internetAddress.getPersonal());

  258.             if (localeRecipients.keySet().contains(recipientsLocales.get(internetAddress))) {
  259.                 localeRecipients.get(recipientsLocales.get(internetAddress)).add(internetAddress);
  260.             } else {
  261.                 Set<InternetAddress> addresses = new HashSet<>();
  262.                 addresses.add(internetAddress);
  263.                 localeRecipients.put(recipientsLocales.get(internetAddress), addresses);
  264.             }
  265.         }

  266.         Iterator<Locale> localesIt = localeRecipients.keySet().iterator();
  267.         try {
  268.             while (localesIt.hasNext()) {
  269.                 Locale currentLocale = localesIt.next();
  270.                 recipients = localeRecipients.get(currentLocale);


  271.                 if (recipients.size() > 0) {
  272.                     String subject;
  273.                     if (type == Type.CREATED) {
  274.                         subject = ITrackerResources.getString(
  275.                                 "itracker.email.issue.subject.created",
  276.                                 currentLocale,
  277.                                 new Object[]{issue.getId(),
  278.                                         issue.getProject().getName()});
  279.                     } else if (type == Type.ASSIGNED) {
  280.                         subject = ITrackerResources.getString(
  281.                                 "itracker.email.issue.subject.assigned",
  282.                                 currentLocale,
  283.                                 new Object[]{issue.getId(),
  284.                                         issue.getProject().getName()});
  285.                     } else if (type == Type.CLOSED) {
  286.                         subject = ITrackerResources.getString(
  287.                                 "itracker.email.issue.subject.closed",
  288.                                 currentLocale,
  289.                                 new Object[]{issue.getId(),
  290.                                         issue.getProject().getName()});
  291.                     } else if (type == Type.ISSUE_REMINDER) {
  292.                         subject = ITrackerResources.getString(
  293.                                 "itracker.email.issue.subject.reminder",
  294.                                 currentLocale,
  295.                                 new Object[]{issue.getId(),
  296.                                         issue.getProject().getName(),
  297.                                         notModifiedSince});
  298.                     } else {
  299.                         subject = ITrackerResources.getString(
  300.                                 "itracker.email.issue.subject.updated",
  301.                                 currentLocale,
  302.                                 new Object[]{issue.getId(),
  303.                                         issue.getProject().getName()});
  304.                     }

  305.                     String activityString;
  306.                     String componentString = "";
  307.                     StringBuilder sb = new StringBuilder();
  308.                     if (activity.size() == 0) {
  309.                         sb.append("-");
  310.                     } else {
  311.                         for (IssueActivity anActivity : activity) {
  312.                             sb.append("\n ").append(
  313.                                     IssueUtilities.getActivityName(anActivity
  314.                                             .getActivityType(), currentLocale)).append(": ").append(
  315.                                     anActivity.getDescription());

  316.                         }
  317.                     }
  318.                     sb.append("\n");
  319.                     activityString = sb.toString();
  320.                     for (int i = 0; i < components.size(); i++) {
  321.                         componentString += (i != 0 ? ", " : "")
  322.                                 + components.get(i).getName();
  323.                     }

  324.                     final String owner = IssueUtilities.getOwnerName(issue.getOwner(), currentLocale);
  325.                     final User hUser = null == history ? null : history.getUser();
  326.                     final String historyUser = (null != hUser) ? hUser.getFullName()
  327.                             : ITrackerResources.getString("itracker.web.generic.notapplicable", currentLocale);

  328.                     final String historyText = (history == null ? "-"
  329.                             : HTMLUtilities
  330.                             .removeMarkup(history
  331.                                     .getDescription()));
  332.                     final String status =
  333.                             IssueUtilities.getStatusName(issue
  334.                                     .getStatus(), currentLocale);
  335.                     final String msgText;
  336.                     if (type == Type.ISSUE_REMINDER) {
  337.                         msgText = ITrackerResources
  338.                                 .getString(
  339.                                         "itracker.email.issue.body.reminder",
  340.                                         currentLocale,
  341.                                         new Object[]{
  342.                                                 IssueUtilities.getIssueURL(issue, url).toExternalForm(),
  343.                                                 issue.getProject().getName(),
  344.                                                 issue.getDescription(),
  345.                                                 IssueUtilities.getStatusName(issue
  346.                                                         .getStatus(), currentLocale),
  347.                                                 IssueUtilities
  348.                                                         .getSeverityName(issue
  349.                                                         .getSeverity(), currentLocale),
  350.                                                 owner,
  351.                                                 componentString,
  352.                                                 historyUser,
  353.                                                 historyText,
  354.                                                 notModifiedSince,
  355.                                                 activityString});
  356.                     } else {
  357.                         String resolution = (issue.getResolution() == null ? ""
  358.                                 : issue.getResolution());
  359.                         if (!resolution.equals("")
  360.                                 && ProjectUtilities
  361.                                 .hasOption(
  362.                                         ProjectUtilities.OPTION_PREDEFINED_RESOLUTIONS,
  363.                                         issue.getProject().getOptions())) {
  364.                             resolution = IssueUtilities.getResolutionName(
  365.                                     resolution, currentLocale);
  366.                         }
  367.                         msgText = ITrackerResources
  368.                                 .getString(
  369.                                         "itracker.email.issue.body."
  370.                                                 + (type == Type.CREATED ? "created" : "standard"),
  371.                                         currentLocale,
  372.                                         new Object[]{
  373.                                                 url + "/module-projects/view_issue.do?id=" + issue.getId(),
  374.                                                 issue.getProject().getName(),
  375.                                                 issue.getDescription(),
  376.                                                 status,
  377.                                                 resolution,
  378.                                                 IssueUtilities
  379.                                                         .getSeverityName(issue
  380.                                                         .getSeverity(), currentLocale),
  381.                                                 owner,
  382.                                                 componentString,
  383.                                                 historyUser,
  384.                                                 historyText,
  385.                                                 activityString,
  386.                                                 recipientsString});
  387.                     }

  388.                     if (logger.isInfoEnabled()) {
  389.                         logger.info("handleNotification: sending notification for " + issue + " (" + type + ") to " + currentLocale + "-users (" + recipients + ")");

  390.                     }
  391.                     for (InternetAddress iadr : recipients) {
  392.                         emailService.sendEmail(iadr, subject, msgText);
  393.                     }

  394.                     if (logger.isDebugEnabled()) {
  395.                         logger.debug("handleNotification: sent notification for " + issue
  396.                                 + ": " + subject + "\n  " + msgText);
  397.                     }
  398.                 }

  399.                 updateIssueActivityNotification(issue, true);
  400.                 if (logger.isDebugEnabled()) {
  401.                     logger.debug("handleNotification: sent notification for locales " + localeRecipients.keySet() + " recipients: " + localeRecipients.values());
  402.                 }
  403.             }
  404.         } catch (RuntimeException e) {
  405.             logger.error("handleNotification: failed to notify: " + issue + " (locales: " + localeRecipients.keySet() + ")", e);

  406.         } catch (MalformedURLException e) {
  407.             logger.error("handleNotification: URL was not well-formed", e);
  408.         }


  409.     }

  410.     private IssueService getIssueService() {
  411.         if (null == issueService) {
  412.             setIssueService((IssueService) applicationContext.getBean("issueService"));
  413.         }

  414.         return issueService;
  415.     }

  416.     public void setIssueService(IssueService issueService) {
  417.         this.issueService = issueService;
  418.     }

  419.     public void updateIssueActivityNotification(Issue issue,
  420.                                                 Boolean notificationSent) {
  421.         if (logger.isDebugEnabled()) {
  422.             logger.debug("updateIssueActivityNotification: called with "
  423.                     + issue + ", notificationSent: " + notificationSent);
  424.         }

  425.         Collection<IssueActivity> activities = getIssueActivityDao()
  426.                 .findByIssueId(issue.getId());
  427.         for (IssueActivity activity : activities) {
  428.             activity.setNotificationSent(notificationSent);
  429.         }
  430.     }

  431.     /**
  432.      */
  433.     public boolean addIssueNotification(Notification notification) {
  434.         if (logger.isDebugEnabled()) {
  435.             logger.debug("addIssueNotification: called with notification: "
  436.                     + notification);
  437.         }
  438.         Issue issue = notification.getIssue();
  439.         if (!issue.getNotifications().contains(notification)) {
  440.             if (notification.getCreateDate() == null) {
  441.                 notification.setCreateDate(new Date());
  442.             }
  443.             if (notification.getLastModifiedDate() == null) {
  444.                 notification.setLastModifiedDate(new Date());
  445.             }

  446.             getNotificationDao().save(notification);

  447.             issue.getNotifications().add(notification);
  448.             getIssueDao().merge(issue);

  449.             return true;
  450.         }
  451.         if (logger.isDebugEnabled()) {
  452.             logger.debug("addIssueNotification: attempted to add duplicate notification " + notification + " for issue: " + issue);
  453.         }
  454.         return false;
  455.     }

  456.     /**
  457.      *
  458.      */
  459.     public List<Notification> getIssueNotifications(Issue issue,
  460.                                                     boolean primaryOnly, boolean activeOnly) {
  461.         if (logger.isDebugEnabled()) {
  462.             logger.debug("getIssueNotifications: called with issue: " + issue
  463.                     + ", primaryOnly: " + primaryOnly + ", activeOnly: "
  464.                     + activeOnly);
  465.         }
  466.         List<Notification> issueNotifications = new ArrayList<>();
  467.         if (issue == null) {
  468.             logger.warn("getIssueNotifications: no issue, throwing exception");
  469.             throw new IllegalArgumentException("issue must not be null");
  470.         }
  471.         if (!primaryOnly) {
  472.             List<Notification> notifications = getNotificationDao()
  473.                     .findByIssueId(issue.getId());

  474.             for (Notification notification : notifications) {
  475.                 User notificationUser = notification.getUser();
  476.                 if (!activeOnly
  477.                         || notificationUser.getStatus() == UserUtilities.STATUS_ACTIVE) {
  478.                     issueNotifications.add(notification);
  479.                 }
  480.             }
  481.         }

  482.         // Now add in other notifications like owner, creator, project owners,
  483.         // etc...

  484.         boolean hasOwner = false;
  485.         if (issue.getOwner() != null) {
  486.             User ownerModel = issue.getOwner();

  487.             if (ownerModel != null
  488.                     && (!activeOnly || ownerModel.getStatus() == UserUtilities.STATUS_ACTIVE)) {
  489.                 issueNotifications.add(new Notification(ownerModel, issue,
  490.                         Role.OWNER));
  491.                 hasOwner = true;
  492.             }
  493.         }

  494.         if (!primaryOnly || !hasOwner) {
  495.             User creatorModel = issue.getCreator();

  496.             if (creatorModel != null
  497.                     && (!activeOnly || creatorModel.getStatus() == UserUtilities.STATUS_ACTIVE)) {
  498.                 issueNotifications.add(new Notification(creatorModel,
  499.                         issue, Role.CREATOR));
  500.             }
  501.         }

  502.         Project project = getProjectService().getProject(
  503.                 issue.getProject().getId());

  504.         for (User projectOwner : project.getOwners()) {
  505.             if (projectOwner != null
  506.                     && (!activeOnly || projectOwner.getStatus() == UserUtilities.STATUS_ACTIVE)) {
  507.                 issueNotifications.add(new Notification(projectOwner,
  508.                         issue, Role.PO));
  509.             }
  510.         }

  511.         if (logger.isDebugEnabled()) {
  512.             logger.debug("getIssueNotifications: returning "
  513.                     + issueNotifications);
  514.         }
  515.         return issueNotifications;
  516.     }

  517.     public List<Notification> getIssueNotifications(Issue issue) {
  518.         if (logger.isDebugEnabled()) {
  519.             logger.debug("getIssueNotifications: called with: " + issue);
  520.         }
  521.         return this.getIssueNotifications(issue, false, true);
  522.     }

  523.     public List<Notification> getPrimaryIssueNotifications(Issue issue) {
  524.         if (logger.isDebugEnabled()) {
  525.             logger.debug("getPrimaryIssueNotifications: called with: " + issue);
  526.         }
  527.         return this.getIssueNotifications(issue, true, false);
  528.     }

  529.     public boolean hasIssueNotification(Issue issue, Integer userId) {
  530.         if (logger.isDebugEnabled()) {
  531.             logger.debug("hasIssueNotification: called with: " + issue
  532.                     + ", userId: " + userId);
  533.         }
  534.         return hasIssueNotification(issue, userId, Role.ANY);
  535.     }

  536.     @Override
  537.     public boolean hasIssueNotification(Issue issue, String login) {

  538.         return hasIssueNotification(issue, login, Role.ANY);
  539.     }

  540.     @Override
  541.     public boolean hasIssueNotification(Issue issue, String login, Role role) {

  542.         if (issue != null && StringUtils.isNotBlank(login)) {

  543.             List<Notification> notifications = getIssueNotifications(issue,
  544.                     false, false);

  545.             for (Notification notification : notifications) {

  546.                 if (role == Role.ANY || notification.getRole() == role) {

  547.                     if (StringUtils.equals(login, notification.getUser().getLogin())) {

  548.                         return true;

  549.                     }

  550.                 }

  551.             }

  552.         }

  553.         return false;
  554.     }

  555.     public boolean hasIssueNotification(Issue issue, Integer userId, Role role) {

  556.         if (issue != null && userId != null) {

  557.             List<Notification> notifications = getIssueNotifications(issue,
  558.                     false, false);

  559.             for (Notification notification : notifications) {

  560.                 if (role == Role.ANY || notification.getRole() == role) {

  561.                     if (notification.getUser().getId().equals(userId)) {

  562.                         return true;

  563.                     }

  564.                 }

  565.             }

  566.         }

  567.         return false;

  568.     }

  569.     public boolean removeIssueNotification(Integer notificationId) {
  570.         Notification notification = this.getNotificationDao().findById(
  571.                 notificationId);
  572.         getNotificationDao().delete(notification);
  573.         return true;
  574.     }

  575.     public void sendNotification(Issue issue, Type type, String baseURL,
  576.                                  InternetAddress[] receipients, Integer lastModifiedDays) {
  577.         this.handleLocalizedIssueNotification(issue, type, baseURL, receipients,
  578.                 lastModifiedDays);

  579.     }


  580.     /**
  581.      * @return the emailService
  582.      */
  583.     public EmailService getEmailService() {
  584.         return emailService;
  585.     }

  586.     /**
  587.      * @return the notificationDao
  588.      */
  589.     private NotificationDAO getNotificationDao() {
  590.         return notificationDao;
  591.     }

  592.     /**
  593.      * @return the projectService
  594.      */
  595.     public ProjectService getProjectService() {
  596.         return projectService;
  597.     }

  598.     /**
  599.      * @param projectService the projectService to set
  600.      */
  601.     public void setProjectService(ProjectService projectService) {
  602.         this.projectService = projectService;
  603.     }

  604.     /**
  605.      * @param notificationDao the notificationDao to set
  606.      */
  607.     public void setNotificationDao(NotificationDAO notificationDao) {
  608.         if (null == notificationDao) {
  609.             throw new IllegalArgumentException(
  610.                     "notification dao must not be null");
  611.         }
  612.         if (null != this.notificationDao) {
  613.             throw new IllegalStateException("notification dao allready set");
  614.         }
  615.         this.notificationDao = notificationDao;
  616.     }


  617.     /**
  618.      * @return the issueActivityDao
  619.      */
  620.     public IssueActivityDAO getIssueActivityDao() {
  621.         return issueActivityDao;
  622.     }

  623.     /**
  624.      * @param issueActivityDao the issueActivityDao to set
  625.      */
  626.     public void setIssueActivityDao(IssueActivityDAO issueActivityDao) {
  627.         this.issueActivityDao = issueActivityDao;
  628.     }

  629.     /**
  630.      * @return the issueDao
  631.      */
  632.     public IssueDAO getIssueDao() {
  633.         return issueDao;
  634.     }

  635.     /**
  636.      * @param issueDao the issueDao to set
  637.      */
  638.     public void setIssueDao(IssueDAO issueDao) {
  639.         this.issueDao = issueDao;
  640.     }

  641.     public String getIssueServiceName() {
  642.         return issueServiceName;
  643.     }

  644.     public void setIssueServiceName(String issueServiceName) {
  645.         this.issueServiceName = issueServiceName;
  646.     }

  647. }