Clover Coverage Report - itracker
Coverage timestamp: Tue May 1 2012 16:42:12 CEST
315   1,089   156   10.5
230   837   0.5   30
30     5.2  
1    
 
 
  NotificationServiceImpl       Line # 60 315 156 0% 0.0
 
No Tests
 
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   
19    package org.itracker.services.implementations;
20   
21    import java.util.ArrayList;
22    import java.util.Arrays;
23    import java.util.Collection;
24    import java.util.Date;
25    import java.util.HashSet;
26    import java.util.Hashtable;
27    import java.util.Iterator;
28    import java.util.List;
29    import java.util.Locale;
30    import java.util.Map;
31    import java.util.Set;
32   
33    import javax.mail.internet.InternetAddress;
34   
35    import org.apache.log4j.Logger;
36    import org.itracker.core.resources.ITrackerResources;
37    import org.itracker.model.Component;
38    import org.itracker.model.Issue;
39    import org.itracker.model.IssueActivity;
40    import org.itracker.model.IssueHistory;
41    import org.itracker.model.Notification;
42    import org.itracker.model.Project;
43    import org.itracker.model.User;
44    import org.itracker.model.Version;
45    import org.itracker.model.Notification.Role;
46    import org.itracker.model.Notification.Type;
47    import org.itracker.persistence.dao.IssueActivityDAO;
48    import org.itracker.persistence.dao.IssueDAO;
49    import org.itracker.persistence.dao.NotificationDAO;
50    import org.itracker.services.IssueService;
51    import org.itracker.services.NotificationService;
52    import org.itracker.services.ProjectService;
53    import org.itracker.services.util.EmailService;
54    import org.itracker.services.util.HTMLUtilities;
55    import org.itracker.services.util.IssueUtilities;
56    import org.itracker.services.util.ProjectUtilities;
57    import org.itracker.services.util.UserUtilities;
58    import org.itracker.web.util.ServletContextUtils;
59   
 
60    public class NotificationServiceImpl implements NotificationService {
61   
62    // TODO: Cleanup this file, go through all issues, todos, etc.
63   
64    private EmailService emailService;
65    private NotificationDAO notificationDao;
66    private ProjectService projectService;
67    private IssueActivityDAO issueActivityDao;
68    private IssueDAO issueDao;
69   
70    private static final Logger logger = Logger
71    .getLogger(NotificationServiceImpl.class);
72   
 
73  0 toggle public NotificationServiceImpl() {
74   
75  0 this.emailService = null;
76  0 this.projectService = null;
77  0 this.notificationDao = null;
78    }
79   
 
80  0 toggle public NotificationServiceImpl(EmailService emailService,
81    ProjectService projectService, NotificationDAO notificationDao, IssueActivityDAO issueActivityDao, IssueDAO issueDao) {
82  0 this();
83  0 this.setEmailService(emailService);
84  0 this.setProjectService(projectService);
85  0 this.setNotificationDao(notificationDao);
86  0 this.setIssueActivityDao(issueActivityDao);
87  0 this.setIssueDao(issueDao);
88    }
89   
 
90  0 toggle public void sendNotification(Notification notification, Type type,
91    String url) {
92   
93  0 if (logger.isDebugEnabled()) {
94  0 logger.debug("sendNotification: called with notification: "
95    + notification + ", type: " + url + ", url: " + url);
96    }
97  0 if (null == notification) {
98  0 throw new IllegalArgumentException("notification must not be null");
99    }
100  0 if (null == this.emailService || null == this.notificationDao) {
101  0 throw new IllegalStateException("service not initialized yet");
102    }
103  0 if (type == Type.SELF_REGISTER) {
104  0 this.handleSelfRegistrationNotification(notification.getUser()
105    .getLogin(), notification.getUser().getEmailAddress(), notification.getUser().getPreferences().getUserLocale(), url);
106    } else {
107  0 handleIssueNotification(notification.getIssue(), type, url);
108   
109    }
110   
111    }
112   
 
113  0 toggle public void sendNotification(Issue issue, Type type, String baseURL) {
114  0 if (logger.isDebugEnabled()) {
115  0 logger.debug("sendNotification: called with issue: " + issue
116    + ", type: " + type + ", baseURL: " + baseURL);
117    }
118  0 handleIssueNotification(issue, type, baseURL);
119   
120    }
121   
 
122  0 toggle public void setEmailService(EmailService emailService) {
123   
124  0 if (null == emailService)
125  0 throw new IllegalArgumentException("email service must not be null");
126   
127  0 if (null != this.emailService) {
128  0 throw new IllegalStateException("email service allready set");
129    }
130  0 this.emailService = emailService;
131   
132    }
133   
134    /**
135    *
136    * @param login
137    * @param toAddress
138    * @param url
139    */
 
140  0 toggle private void handleSelfRegistrationNotification(String login,
141    InternetAddress toAddress, String locale, String url) {
142  0 if (logger.isDebugEnabled()) {
143  0 logger
144    .debug("handleSelfRegistrationNotification: called with login: "
145    + login
146    + ", toAddress"
147    + toAddress
148    + ", url: "
149    + url);
150    }
151  0 try {
152   
153  0 if (toAddress != null && !"".equals(toAddress.getAddress())) {
154  0 String subject = ITrackerResources
155    .getString("itracker.email.selfreg.subject", locale);
156  0 String msgText = ITrackerResources.getString(
157    "itracker.email.selfreg.body", locale, new Object[] { login,
158    url + "/login.do" });
159  0 emailService.sendEmail(toAddress, subject, msgText);
160    } else {
161  0 throw new IllegalArgumentException(
162    "To-address must be set for self registration notification.");
163    }
164    } catch (RuntimeException e) {
165  0 logger.error("failed to handle self registration notification for "
166    + toAddress, e);
167  0 throw e;
168    }
169    }
170   
171    /**
172    * Method for internal sending of a notification of specific type.
173    *
174    * @param issue
175    * @param type
176    * @param url
177    */
 
178  0 toggle private void handleIssueNotification(Issue issue, Type type, String url) {
179   
180  0 if (logger.isDebugEnabled()) {
181  0 logger.debug("handleIssueNotification: called with issue: " + issue
182    + ", type: " + type + "url: " + url);
183    }
184  0 this.handleLocalizedIssueNotification(issue, type, url,null, null);
185    //this.handleIssueNotification(issue, type, url, null, null);
186    }
187   
188    /**
189    * Method for internal sending of a notification of specific type.
190    *
191    * @param issue
192    * @param type
193    * @param url
194    */
 
195  0 toggle private void handleIssueNotification(Issue issue, Type type, String url,
196    InternetAddress[] recipients, Integer lastModifiedDays) {
197  0 try {
198   
199  0 if (logger.isDebugEnabled()) {
200  0 logger
201    .debug("handleIssueNotificationhandleIssueNotification: called with issue: "
202    + issue
203    + ", type: "
204    + type
205    + "url: "
206    + url
207    + ", recipients: "
208  0 + (null == recipients ? "<null>" : String
209    .valueOf(Arrays.asList(recipients)))
210    + ", lastModifiedDays: " + lastModifiedDays);
211    }
212  0 List<Notification> notifications;
213   
214  0 if (issue == null) {
215  0 logger
216    .warn("handleIssueNotification: issue was null. Notification will not be handled");
217  0 return;
218    }
219   
220  0 if (lastModifiedDays == null || lastModifiedDays.intValue() < 0) {
221  0 lastModifiedDays = Integer
222    .valueOf(org.itracker.web.scheduler.tasks.ReminderNotification.DEFAULT_ISSUE_AGE);
223    }
224   
225  0 if (recipients == null) {
226  0 ArrayList<InternetAddress> recList = new ArrayList<InternetAddress>();
227  0 notifications = this.getIssueNotifications(issue);
228  0 Iterator<Notification> it = notifications.iterator();
229  0 User currentUser;
230  0 while (it.hasNext()) {
231  0 currentUser = it.next().getUser();
232  0 if (null != currentUser
233    && null != currentUser.getEmailAddress()
234    && null != currentUser.getEmail()
235    && (!recList
236    .contains(currentUser.getEmailAddress()))
237    && currentUser.getEmail().indexOf('@') >= 0) {
238   
239  0 recList.add(currentUser.getEmailAddress());
240    }
241    }
242  0 recipients = recList.toArray(new InternetAddress[] {});
243    }
244   
245  0 List<IssueActivity> activity = getIssueService().getIssueActivity(
246    issue.getId(), false);
247  0 issue.getActivities();
248  0 List<IssueHistory> histories = issue.getHistory();
249  0 Iterator<IssueHistory> it = histories.iterator();
250  0 IssueHistory history = null, currentHistory;
251  0 history = getIssueService().getLastIssueHistory(issue.getId());
252   
253  0 Integer historyId = 0;
254    // find history with greatest id
255  0 while (it.hasNext()) {
256  0 currentHistory = (IssueHistory) it.next();
257  0 if (logger.isDebugEnabled()) {
258  0 logger.debug("handleIssueNotification: found history: "
259    + currentHistory.getDescription() + " (time: "
260    + currentHistory.getCreateDate());
261    }
262  0 if (currentHistory.getId() > historyId) {
263  0 historyId = currentHistory.getId();
264  0 history = currentHistory;
265    }
266    }
267  0 if (logger.isDebugEnabled() && null != history) {
268  0 logger
269    .debug("handleIssueNotification: got most recent history: "
270    + history
271    + " ("
272    + history.getDescription()
273    + ")");
274    }
275   
276  0 List<Component> components = issue.getComponents();
277    // issueService
278    // .getIssueComponents(issue.getId());
279   
280  0 List<Version> versions = issue.getVersions();
281    // issueService.getIssueVersions(issue.getId());
282   
283  0 if (recipients.length > 0) {
284  0 String subject = "";
285  0 if (type == Type.CREATED) {
286  0 subject = ITrackerResources.getString(
287    "itracker.email.issue.subject.created",
288    new Object[] { issue.getId(),
289    issue.getProject().getName(),
290    lastModifiedDays });
291  0 } else if (type == Type.ASSIGNED) {
292  0 subject = ITrackerResources.getString(
293    "itracker.email.issue.subject.assigned",
294    new Object[] { issue.getId(),
295    issue.getProject().getName(),
296    lastModifiedDays });
297  0 } else if (type == Type.CLOSED) {
298  0 subject = ITrackerResources.getString(
299    "itracker.email.issue.subject.closed",
300    new Object[] { issue.getId(),
301    issue.getProject().getName(),
302    lastModifiedDays });
303  0 } else if (type == Type.ISSUE_REMINDER) {
304  0 subject = ITrackerResources.getString(
305    "itracker.email.issue.subject.reminder",
306    new Object[] { issue.getId(),
307    issue.getProject().getName(),
308    lastModifiedDays });
309    } else {
310  0 subject = ITrackerResources.getString(
311    "itracker.email.issue.subject.updated",
312    new Object[] { issue.getId(),
313    issue.getProject().getName(),
314    lastModifiedDays });
315    }
316   
317  0 String activityString;
318  0 String componentString = "";
319  0 String versionString = "";
320  0 StringBuffer sb = new StringBuffer();
321  0 for (int i = 0; i < activity.size(); i++) {
322  0 sb.append(
323    IssueUtilities.getActivityName(activity.get(i)
324    .getActivityType())).append(": ").append(
325    activity.get(i).getDescription()).append("\n");
326   
327    }
328  0 activityString = sb.toString();
329  0 for (int i = 0; i < components.size(); i++) {
330  0 componentString += (i != 0 ? ", " : "")
331    + components.get(i).getName();
332    }
333  0 for (int i = 0; i < versions.size(); i++) {
334  0 versionString += (i != 0 ? ", " : "")
335    + versions.get(i).getNumber();
336    }
337   
338  0 String msgText = "";
339  0 if (type == Type.ISSUE_REMINDER) {
340  0 msgText = ITrackerResources
341    .getString(
342    "itracker.email.issue.body.reminder",
343    new Object[] {
344    url
345    + "/module-projects/view_issue.do?id="
346    + issue.getId(),
347    issue.getProject().getName(),
348    issue.getDescription(),
349    IssueUtilities.getStatusName(issue
350    .getStatus()),
351    IssueUtilities
352    .getSeverityName(issue
353    .getSeverity()),
354  0 (issue.getOwner().getFirstName() != null ? issue
355    .getOwner().getFirstName()
356    : "")
357    + " "
358  0 + (issue.getOwner()
359    .getLastName() != null ? issue
360    .getOwner()
361    .getLastName()
362    : ""),
363    componentString,
364  0 (history == null ? "" : history
365    .getUser().getFirstName()
366    + " "
367    + history.getUser()
368    .getLastName()),
369  0 (history == null ? ""
370    : HTMLUtilities
371    .removeMarkup(history
372    .getDescription())),
373    lastModifiedDays, activityString });
374    } else {
375  0 String resolution = (issue.getResolution() == null ? ""
376    : issue.getResolution());
377  0 if (!resolution.equals("")
378    && ProjectUtilities
379    .hasOption(
380    ProjectUtilities.OPTION_PREDEFINED_RESOLUTIONS,
381    issue.getProject().getOptions())) {
382  0 resolution = IssueUtilities.getResolutionName(
383    resolution, ITrackerResources.getLocale());
384    }
385   
386  0 msgText = ITrackerResources
387    .getString(
388    "itracker.email.issue.body.standard",
389    new Object[] {
390    new StringBuffer(url).append("/module-projects/view_issue.do?id=").append(issue.getId()).toString(),
391    issue.getProject().getName(),
392    issue.getDescription(),
393    IssueUtilities.getStatusName(issue
394    .getStatus()),
395    resolution,
396    IssueUtilities
397    .getSeverityName(issue
398    .getSeverity()),
399  0 (null != issue.getOwner() && null != issue.getOwner().getFirstName() ? issue
400    .getOwner().getFirstName()
401    : "")
402    + " "
403  0 + (null != issue.getOwner() && null != issue.getOwner()
404    .getLastName() ? issue
405    .getOwner()
406    .getLastName()
407    : ""),
408    componentString,
409  0 (history == null ? "" : history
410    .getUser().getFirstName()
411    + " "
412    + history.getUser()
413    .getLastName()),
414  0 (history == null ? ""
415    : HTMLUtilities
416    .removeMarkup(history
417    .getDescription())),
418    activityString });
419    }
420  0 emailService.sendEmail(recipients, subject, msgText);
421   
422  0 updateIssueActivityNotification(issue, true);
423    }
424   
425    } catch (Exception e) {
426  0 logger
427    .error(
428    "handleIssueNotification: unexpected exception caught, throwing runtime exception",
429    e);
430  0 throw new RuntimeException(e);
431    }
432    }
433   
434   
435    /**
436    * Method for internal sending of a notification of specific type.
437    *
438    * TODO: final debugging/integration/implementation
439    * TODO: Decide if this code is really needed and document for what
440    *
441    * @param issue
442    * @param type
443    * @param url
444    */
445   
 
446  0 toggle @SuppressWarnings("unused")
447    private void handleLocalizedIssueNotification(final Issue issue, final Type type, final String url,
448    final InternetAddress[] recipients, Integer lastModifiedDays) {
449  0 try {
450   
451  0 if (logger.isDebugEnabled()) {
452  0 logger
453    .debug("handleIssueNotificationhandleIssueNotification: running as thread, called with issue: "
454    + issue
455    + ", type: "
456    + type
457    + "url: "
458    + url
459    + ", recipients: "
460  0 + (null == recipients ? "<null>" : String
461    .valueOf(Arrays.asList(recipients)))
462    + ", lastModifiedDays: " + lastModifiedDays);
463    }
464   
465  0 final Integer notModifiedSince;
466   
467  0 if (lastModifiedDays == null || lastModifiedDays.intValue() < 0) {
468  0 notModifiedSince = Integer
469    .valueOf(org.itracker.web.scheduler.tasks.ReminderNotification.DEFAULT_ISSUE_AGE);
470    } else {
471  0 notModifiedSince = lastModifiedDays;
472    }
473   
474  0 try {
475  0 if (logger.isDebugEnabled()) {
476  0 logger
477    .debug("handleIssueNotificationhandleIssueNotification.run: running as thread, called with issue: "
478    + issue
479    + ", type: "
480    + type
481    + "url: "
482    + url
483    + ", recipients: "
484  0 + (null == recipients ? "<null>" : String
485    .valueOf(Arrays.asList(recipients)))
486    + ", notModifiedSince: " + notModifiedSince);
487    }
488  0 final List<Notification> notifications;
489  0 if (issue == null) {
490  0 logger
491    .warn("handleIssueNotification: issue was null. Notification will not be handled");
492  0 return;
493    }
494  0 Map<InternetAddress, Locale> localeMapping = null;
495   
496  0 if (recipients == null) {
497   
498   
499  0 notifications = this.getIssueNotifications(issue);
500   
501  0 localeMapping = new Hashtable<InternetAddress, Locale>(notifications.size());
502  0 Iterator<Notification> it = notifications.iterator();
503  0 User currentUser;
504  0 while (it.hasNext()) {
505  0 currentUser = it.next().getUser();
506  0 if (null != currentUser
507    && null != currentUser.getEmailAddress()
508    && null != currentUser.getEmail()
509    && (!localeMapping.keySet()
510    .contains(currentUser.getEmailAddress()))) {
511   
512  0 try {
513  0 localeMapping.put(currentUser.getEmailAddress(), ITrackerResources.getLocale(currentUser.getPreferences().getUserLocale()));
514    } catch (RuntimeException re) {
515  0 localeMapping.put(currentUser.getEmailAddress(), ITrackerResources.getLocale());
516    }
517    }
518    }
519    } else {
520  0 localeMapping = new Hashtable<InternetAddress, Locale>(1);
521  0 Locale locale = ITrackerResources.getLocale();
522  0 Iterator<InternetAddress> it = Arrays.asList(recipients).iterator();
523  0 while (it.hasNext()) {
524  0 InternetAddress internetAddress = (InternetAddress) it
525    .next();
526  0 localeMapping.put(internetAddress, locale);
527    }
528    }
529   
530  0 this.handleNotification(issue, type, notModifiedSince, localeMapping, url);
531    } catch (Exception e) {
532  0 logger.error("run: failed to process notification", e);
533    }
534   
535    } catch (Exception e) {
536  0 logger
537    .error(
538    "handleIssueNotification: unexpected exception caught, throwing runtime exception",
539    e);
540  0 throw new RuntimeException(e);
541    }
542    }
543   
544    /**
545    * Send notifications to mapped addresses by locale.
546    * @param issue
547    * @param type
548    * @param notModifiedSince
549    * @param recipientsLocales
550    * @param url
551    */
 
552  0 toggle private void handleNotification(Issue issue, Type type, Integer notModifiedSince, Map<InternetAddress, Locale> recipientsLocales, final String url) {
553  0 Set<InternetAddress> recipients = recipientsLocales.keySet();
554  0 Map<Locale, Set<InternetAddress>> localeRecipients = new Hashtable<Locale, Set<InternetAddress>>();
555   
556  0 List<Component> components = issue.getComponents();
557  0 List<Version> versions = issue.getVersions();
558   
559  0 List<IssueActivity> activity = getIssueService().getIssueActivity(
560    issue.getId(), false);
561   
562  0 List<IssueHistory> histories = issue.getHistory();
563  0 Iterator<IssueHistory> it = histories.iterator();
564  0 IssueHistory history = null, currentHistory;
565  0 history = getIssueService().getLastIssueHistory(issue.getId());
566  0 StringBuilder recipientsString = new StringBuilder();
567   
568  0 Integer historyId = 0;
569    // find history with greatest id
570  0 while (it.hasNext()) {
571  0 currentHistory = (IssueHistory) it.next();
572  0 if (logger.isDebugEnabled()) {
573  0 logger.debug("handleIssueNotification: found history: "
574    + currentHistory.getDescription() + " (time: "
575    + currentHistory.getCreateDate());
576    }
577  0 if (currentHistory.getId() > historyId) {
578  0 historyId = currentHistory.getId();
579  0 history = currentHistory;
580    }
581    }
582  0 if (logger.isDebugEnabled() && null != history) {
583  0 logger
584    .debug("handleIssueNotification: got most recent history: "
585    + history
586    + " ("
587    + history.getDescription()
588    + ")");
589    }
590   
591  0 Iterator<InternetAddress> iaIt = recipientsLocales.keySet().iterator();
592  0 while (iaIt.hasNext()) {
593  0 InternetAddress internetAddress = (InternetAddress) iaIt.next();
594   
595  0 recipientsString.append("\n ");
596  0 recipientsString.append(internetAddress.getPersonal());
597   
598  0 if (localeRecipients.keySet().contains(recipientsLocales.get(internetAddress))) {
599  0 localeRecipients.get(recipientsLocales.get(internetAddress)).add(internetAddress);
600    } else {
601  0 Set<InternetAddress> addresses = new HashSet<InternetAddress>();
602  0 addresses.add(internetAddress);
603  0 localeRecipients.put(recipientsLocales.get(internetAddress), addresses);
604    }
605    }
606   
607  0 Iterator<Locale> localesIt = localeRecipients.keySet().iterator();
608  0 try {
609  0 while (localesIt.hasNext()) {
610  0 Locale currentLocale = (Locale) localesIt.next();
611  0 recipients = localeRecipients.get(currentLocale);
612   
613   
614  0 if (recipients.size() > 0) {
615  0 String subject = "";
616  0 if (type == Type.CREATED) {
617  0 subject = ITrackerResources.getString(
618    "itracker.email.issue.subject.created",
619    currentLocale,
620    new Object[] { issue.getId(),
621    issue.getProject().getName(),
622    notModifiedSince });
623  0 } else if (type == Type.ASSIGNED) {
624  0 subject = ITrackerResources.getString(
625    "itracker.email.issue.subject.assigned",
626    currentLocale,
627    new Object[] { issue.getId(),
628    issue.getProject().getName(),
629    notModifiedSince });
630  0 } else if (type == Type.CLOSED) {
631  0 subject = ITrackerResources.getString(
632    "itracker.email.issue.subject.closed",
633    currentLocale,
634    new Object[] { issue.getId(),
635    issue.getProject().getName(),
636    notModifiedSince });
637  0 } else if (type == Type.ISSUE_REMINDER) {
638  0 subject = ITrackerResources.getString(
639    "itracker.email.issue.subject.reminder",
640    currentLocale,
641    new Object[] { issue.getId(),
642    issue.getProject().getName(),
643    notModifiedSince });
644    } else {
645  0 subject = ITrackerResources.getString(
646    "itracker.email.issue.subject.updated",
647    currentLocale,
648    new Object[] { issue.getId(),
649    issue.getProject().getName(),
650    notModifiedSince });
651    }
652   
653  0 String activityString;
654  0 String componentString = "";
655  0 String versionString = "";
656  0 StringBuffer sb = new StringBuffer();
657  0 if (activity.size()==0) {
658  0 sb.append("-");
659    } else {
660  0 for (int i = 0; i < activity.size(); i++) {
661  0 sb.append("\n ").append(
662    IssueUtilities.getActivityName(activity.get(i)
663    .getActivityType(), currentLocale)).append(": ").append(
664    activity.get(i).getDescription());
665   
666    }
667    }
668  0 sb.append("\n");
669  0 activityString = sb.toString();
670    // TODO localize..
671  0 for (int i = 0; i < components.size(); i++) {
672  0 componentString += (i != 0 ? ", " : "")
673    + components.get(i).getName();
674    }
675  0 for (int i = 0; i < versions.size(); i++) {
676  0 versionString += (i != 0 ? ", " : "")
677    + versions.get(i).getNumber();
678    }
679   
680  0 String msgText = "";
681  0 if (type == Type.ISSUE_REMINDER) {
682  0 msgText = ITrackerResources
683    .getString(
684    "itracker.email.issue.body.reminder",
685    currentLocale,
686    new Object[] {
687    url
688    + "/module-projects/view_issue.do?id="
689    + issue.getId(),
690    issue.getProject().getName(),
691    issue.getDescription(),
692    IssueUtilities.getStatusName(issue
693    .getStatus(), currentLocale),
694    IssueUtilities
695    .getSeverityName(issue
696    .getSeverity(), currentLocale),
697  0 (issue.getOwner().getFirstName() != null ? issue
698    .getOwner().getFirstName()
699    : "")
700    + " "
701  0 + (issue.getOwner()
702    .getLastName() != null ? issue
703    .getOwner()
704    .getLastName()
705    : ""),
706    componentString,
707  0 (history == null ? "" : history
708    .getUser().getFirstName()
709    + " "
710    + history.getUser()
711    .getLastName()),
712  0 (history == null ? ""
713    : HTMLUtilities
714    .removeMarkup(history
715    .getDescription())),
716    notModifiedSince, activityString });
717    } else {
718  0 String resolution = (issue.getResolution() == null ? ""
719    : issue.getResolution());
720  0 if (!resolution.equals("")
721    && ProjectUtilities
722    .hasOption(
723    ProjectUtilities.OPTION_PREDEFINED_RESOLUTIONS,
724    issue.getProject().getOptions())) {
725  0 resolution = IssueUtilities.getResolutionName(
726    resolution, currentLocale);
727    }
728  0 User oUser = issue.getOwner();
729  0 String owner = (null != oUser && null != oUser.getFirstName()
730    ? oUser.getFirstName()
731    : "")
732    + " "
733  0 + (null != oUser && null != oUser.getLastName()
734    ? oUser.getLastName()
735    : "");
736  0 User hUser = null==history?null:history.getUser();
737  0 String historyUser = (null != hUser && null != hUser.getFirstName()
738    ? hUser.getFirstName()
739    : "")
740    + " "
741  0 + (null != hUser && null != hUser.getLastName()
742    ? hUser.getLastName()
743    : "");
744  0 msgText = ITrackerResources
745    .getString(
746    "itracker.email.issue.body.standard",
747    currentLocale,
748    new Object[] {
749    new StringBuffer(url).append("/module-projects/view_issue.do?id=").append(issue.getId()).toString(),
750    issue.getProject().getName(),
751    issue.getDescription(),
752    IssueUtilities.getStatusName(issue
753    .getStatus(), currentLocale),
754    resolution,
755    IssueUtilities
756    .getSeverityName(issue
757    .getSeverity(), currentLocale),
758    owner,
759    componentString,
760    historyUser,
761  0 (history == null ? ""
762    : HTMLUtilities
763    .removeMarkup(history
764    .getDescription())),
765    activityString,
766    recipientsString});
767    }
768   
769  0 if (logger.isInfoEnabled()) {
770  0 logger.info(new StringBuilder("handleNotification: sending notification for ").append(issue).append(" (").append(type).append(") to ").append(currentLocale).append("-users (").append(recipients + ")").toString());
771   
772    }
773  0 for (InternetAddress iadr: recipients) {
774  0 emailService.sendEmail(iadr, subject, msgText);
775    }
776   
777  0 if (logger.isDebugEnabled()) {
778  0 logger.debug("handleNotification: sent notification for " + issue
779    + ": " + subject + "\n " + msgText);
780    }
781    }
782   
783  0 updateIssueActivityNotification(issue, true);
784  0 if (logger.isDebugEnabled()) {
785  0 logger.debug("handleNotification: sent notification for locales " + localeRecipients.keySet() + " recipients: " + localeRecipients.values());
786    }
787    }
788    } catch (RuntimeException e) {
789  0 logger.error("handleNotification: failed to notify: " + issue + " (locales: " + localeRecipients.keySet() + ")", e);
790   
791    }
792   
793   
794    }
 
795  0 toggle private IssueService getIssueService() {
796  0 return ServletContextUtils.getItrackerServices().getIssueService();
797    }
798   
 
799  0 toggle public void updateIssueActivityNotification(Issue issue,
800    Boolean notificationSent) {
801  0 if (logger.isDebugEnabled()) {
802  0 logger.debug("updateIssueActivityNotification: called with "
803    + issue + ", notificationSent: " + notificationSent);
804    }
805   
806  0 Collection<IssueActivity> activity = getIssueActivityDao()
807    .findByIssueId(issue.getId());
808   
809  0 for (Iterator<IssueActivity> iter = activity.iterator(); iter.hasNext();) {
810   
811  0 ((IssueActivity) iter.next()).setNotificationSent(notificationSent);
812   
813    }
814    }
815   
816    /**
817    */
 
818  0 toggle public boolean addIssueNotification(Notification notification) {
819  0 if (logger.isDebugEnabled()) {
820  0 logger.debug("addIssueNotification: called with notification: "
821    + notification);
822    }
823  0 Issue issue = notification.getIssue();
824  0 if (!issue.getNotifications().contains(notification)) {
825  0 if (notification.getCreateDate() == null) {
826  0 notification.setCreateDate(new Date());
827    }
828  0 if (notification.getLastModifiedDate() == null) {
829  0 notification.setLastModifiedDate(new Date());
830    }
831    // List<Notification> notifications = new ArrayList<Notification>();
832    // TODO: check these 3 lines - do we need them?:
833    // notifications.addAll(issue.getNotifications());
834    // notifications.add(notification);
835    // issue.setNotifications(notifications);
836  0 getNotificationDao().save(notification);
837    // TODO: is it needed to update issue too?
838  0 issue.getNotifications().add(notification);
839  0 getIssueDao().merge(issue);
840   
841  0 return true;
842    }
843  0 if (logger.isDebugEnabled()) {
844  0 logger.debug("addIssueNotification: attempted to add duplicate notification " + notification + " for issue: " + issue);
845    }
846  0 return false;
847    }
848   
849    /**
850    *
851    */
 
852  0 toggle public List<Notification> getIssueNotifications(Issue issue,
853    boolean primaryOnly, boolean activeOnly) {
854  0 if (logger.isDebugEnabled()) {
855  0 logger.debug("getIssueNotifications: called with issue: " + issue
856    + ", primaryOnly: " + primaryOnly + ", activeOnly: "
857    + activeOnly);
858    }
859  0 List<Notification> issueNotifications = new ArrayList<Notification>();
860  0 if (issue == null) {
861  0 logger.warn("getIssueNotifications: no issue, throwing exception");
862  0 throw new IllegalArgumentException("issue must not be null");
863    }
864  0 if (!primaryOnly) {
865  0 List<Notification> notifications = getNotificationDao()
866    .findByIssueId(issue.getId());
867   
868  0 for (Iterator<Notification> iterator = notifications.iterator(); iterator
869    .hasNext();) {
870  0 Notification notification = iterator.next();
871  0 User notificationUser = notification.getUser();
872   
873  0 if (!activeOnly
874    || notificationUser.getStatus() == UserUtilities.STATUS_ACTIVE) {
875  0 issueNotifications.add(notification);
876    }
877    }
878    }
879   
880    // Now add in other notifications like owner, creator, project owners,
881    // etc...
882   
883  0 boolean hasOwner = false;
884    // getIssueDAO().findByPrimaryKey(issueId);
885  0 if (issue != null) {
886  0 if (issue.getOwner() != null) {
887  0 User ownerModel = issue.getOwner();
888   
889  0 if (ownerModel != null
890    && (!activeOnly || ownerModel.getStatus() == UserUtilities.STATUS_ACTIVE)) {
891  0 issueNotifications.add(new Notification(ownerModel, issue,
892    Role.OWNER));
893  0 hasOwner = true;
894    }
895    }
896   
897  0 if (!primaryOnly || !hasOwner) {
898  0 User creatorModel = issue.getCreator();
899   
900  0 if (creatorModel != null
901    && (!activeOnly || creatorModel.getStatus() == UserUtilities.STATUS_ACTIVE)) {
902  0 issueNotifications.add(new Notification(creatorModel,
903    issue, Role.CREATOR));
904    }
905    }
906   
907  0 Project project = getProjectService().getProject(
908    issue.getProject().getId());
909  0 Collection<User> projectOwners = project.getOwners();
910   
911  0 for (Iterator<User> iterator = projectOwners.iterator(); iterator
912    .hasNext();) {
913  0 User projectOwner = (User) iterator.next();
914   
915  0 if (projectOwner != null
916    && (!activeOnly || projectOwner.getStatus() == UserUtilities.STATUS_ACTIVE)) {
917  0 issueNotifications.add(new Notification(projectOwner,
918    issue, Role.PO));
919    }
920    }
921    }
922   
923  0 if (logger.isDebugEnabled()) {
924  0 logger.debug("getIssueNotifications: returning "
925    + issueNotifications);
926    }
927  0 return issueNotifications;
928    }
929   
 
930  0 toggle public List<Notification> getIssueNotifications(Issue issue) {
931  0 if (logger.isDebugEnabled()) {
932  0 logger.debug("getIssueNotifications: called with: " + issue);
933    }
934  0 return this.getIssueNotifications(issue, false, true);
935    }
936   
 
937  0 toggle public List<Notification> getPrimaryIssueNotifications(Issue issue) {
938  0 if (logger.isDebugEnabled()) {
939  0 logger.debug("getPrimaryIssueNotifications: called with: " + issue);
940    }
941  0 return this.getIssueNotifications(issue, true, false);
942    }
943   
 
944  0 toggle public boolean hasIssueNotification(Issue issue, Integer userId) {
945  0 if (logger.isDebugEnabled()) {
946  0 logger.debug("hasIssueNotification: called with: " + issue
947    + ", userId: " + userId);
948    }
949  0 return hasIssueNotification(issue, userId, Role.ANY);
950    }
951   
952    /**
953    * @param issue
954    * @param userId
955    * @param role
956    * @return
957    */
 
958  0 toggle public boolean hasIssueNotification(Issue issue, Integer userId, Role role) {
959   
960  0 if (issue != null && userId != null) {
961   
962  0 List<Notification> notifications = getIssueNotifications(issue,
963    false, false);
964   
965  0 for (int i = 0; i < notifications.size(); i++) {
966   
967  0 if (role == Role.ANY || notifications.get(i).getRole() == role) {
968   
969  0 if (notifications.get(i).getUser().getId().equals(userId)) {
970   
971  0 return true;
972   
973    }
974   
975    }
976   
977    }
978   
979    }
980   
981  0 return false;
982   
983    }
984   
 
985  0 toggle public boolean removeIssueNotification(Integer notificationId) {
986  0 Notification notification = this.getNotificationDao().findById(
987    notificationId);
988  0 getNotificationDao().delete(notification);
989  0 return true;
990    }
991   
 
992  0 toggle public void sendNotification(Issue issue, Type type, String baseURL,
993    InternetAddress[] receipients, Integer lastModifiedDays) {
994  0 this.handleIssueNotification(issue, type, baseURL, receipients,
995    lastModifiedDays);
996   
997    }
998   
999   
1000   
1001    /**
1002    * @return the emailService
1003    */
 
1004  0 toggle public EmailService getEmailService() {
1005  0 return emailService;
1006    }
1007   
1008    /**
1009    * @return the notificationDao
1010    */
 
1011  0 toggle private NotificationDAO getNotificationDao() {
1012  0 return notificationDao;
1013    }
1014   
1015    /**
1016    * @return the projectService
1017    */
 
1018  0 toggle public ProjectService getProjectService() {
1019  0 return projectService;
1020    }
1021   
1022    /**
1023    * @param projectService
1024    * the projectService to set
1025    */
 
1026  0 toggle public void setProjectService(ProjectService projectService) {
1027  0 this.projectService = projectService;
1028    }
1029   
1030    /**
1031    * @param notificationDao
1032    * the notificationDao to set
1033    */
 
1034  0 toggle public void setNotificationDao(NotificationDAO notificationDao) {
1035  0 if (null == notificationDao) {
1036  0 throw new IllegalArgumentException(
1037    "notification dao must not be null");
1038    }
1039  0 if (null != this.notificationDao) {
1040  0 throw new IllegalStateException("notification dao allready set");
1041    }
1042  0 this.notificationDao = notificationDao;
1043    }
1044   
1045    /**
1046    * TODO url should be automatically generated by configuration (baseurl) and
1047    * notification (issue-id).
1048    *
1049    * @param notificationId
1050    * @param url
1051    */
 
1052  0 toggle public void sendNotification(Integer notificationId, Type type, String url) {
1053   
1054  0 Notification notification = notificationDao.findById(notificationId);
1055  0 this.sendNotification(notification, type, url);
1056   
1057    }
1058   
1059    /**
1060    * @return the issueActivityDao
1061    */
 
1062  0 toggle public IssueActivityDAO getIssueActivityDao() {
1063  0 return issueActivityDao;
1064    }
1065   
1066    /**
1067    * @param issueActivityDao
1068    * the issueActivityDao to set
1069    */
 
1070  0 toggle public void setIssueActivityDao(IssueActivityDAO issueActivityDao) {
1071  0 this.issueActivityDao = issueActivityDao;
1072    }
1073   
1074    /**
1075    * @return the issueDao
1076    */
 
1077  0 toggle public IssueDAO getIssueDao() {
1078  0 return issueDao;
1079    }
1080   
1081    /**
1082    * @param issueDao
1083    * the issueDao to set
1084    */
 
1085  0 toggle public void setIssueDao(IssueDAO issueDao) {
1086  0 this.issueDao = issueDao;
1087    }
1088   
1089    }