NotificationServiceImpl.java
- /*
- * This software was designed and created by Jason Carroll.
- * Copyright (c) 2002, 2003, 2004 Jason Carroll.
- * The author can be reached at jcarroll@cowsultants.com
- * ITracker website: http://www.cowsultants.com
- * ITracker forums: http://www.cowsultants.com/phpBB/index.php
- *
- * This program is free software; you can redistribute it and/or modify
- * it only under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- package org.itracker.services.implementations;
- import org.apache.commons.lang.StringUtils;
- import org.apache.log4j.Logger;
- import org.itracker.core.resources.ITrackerResources;
- import org.itracker.model.*;
- import org.itracker.model.Notification.Role;
- import org.itracker.model.Notification.Type;
- import org.itracker.model.util.IssueUtilities;
- import org.itracker.model.util.ProjectUtilities;
- import org.itracker.model.util.UserUtilities;
- import org.itracker.persistence.dao.IssueActivityDAO;
- import org.itracker.persistence.dao.IssueDAO;
- import org.itracker.persistence.dao.NotificationDAO;
- import org.itracker.services.EmailService;
- import org.itracker.services.IssueService;
- import org.itracker.services.NotificationService;
- import org.itracker.services.ProjectService;
- import org.itracker.util.HTMLUtilities;
- import org.springframework.beans.BeansException;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.ApplicationContextAware;
- import javax.mail.internet.InternetAddress;
- import java.net.MalformedURLException;
- import java.util.*;
- public class NotificationServiceImpl implements NotificationService, ApplicationContextAware {
- public static final Integer DEFAULT_ISSUE_AGE = 30;
- private EmailService emailService;
- private NotificationDAO notificationDao;
- private ProjectService projectService;
- private IssueActivityDAO issueActivityDao;
- private IssueDAO issueDao;
- private String issueServiceName;
- private static final Logger logger = Logger
- .getLogger(NotificationServiceImpl.class);
- private IssueService issueService;
- private ApplicationContext applicationContext;
- public NotificationServiceImpl() {
- this.emailService = null;
- this.projectService = null;
- this.notificationDao = null;
- }
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- this.applicationContext = applicationContext;
- }
- public NotificationServiceImpl(EmailService emailService,
- ProjectService projectService, NotificationDAO notificationDao, IssueActivityDAO issueActivityDao, IssueDAO issueDao, IssueService issueService) {
- this();
- this.setEmailService(emailService);
- this.setProjectService(projectService);
- this.setNotificationDao(notificationDao);
- this.setIssueActivityDao(issueActivityDao);
- this.setIssueDao(issueDao);
- this.setIssueService(issueService);
- }
- public void sendNotification(Notification notification, Type type,
- String url) {
- if (logger.isDebugEnabled()) {
- logger.debug("sendNotification: called with notification: "
- + notification + ", type: " + url + ", url: " + url);
- }
- if (null == notification) {
- throw new IllegalArgumentException("notification must not be null");
- }
- if (null == this.emailService || null == this.notificationDao) {
- throw new IllegalStateException("service not initialized yet");
- }
- if (type == Type.SELF_REGISTER) {
- this.handleSelfRegistrationNotification(notification.getUser()
- .getLogin(), notification.getUser().getEmailAddress(), notification.getUser().getPreferences().getUserLocale(), url);
- } else {
- handleIssueNotification(notification.getIssue(), type, url);
- }
- }
- public void sendNotification(Issue issue, Type type, String baseURL) {
- if (logger.isDebugEnabled()) {
- logger.debug("sendNotification: called with issue: " + issue
- + ", type: " + type + ", baseURL: " + baseURL);
- }
- handleIssueNotification(issue, type, baseURL);
- }
- public void setEmailService(EmailService emailService) {
- if (null == emailService)
- throw new IllegalArgumentException("email service must not be null");
- if (null != this.emailService) {
- throw new IllegalStateException("email service allready set");
- }
- this.emailService = emailService;
- }
- private void handleSelfRegistrationNotification(String login,
- InternetAddress toAddress, String locale, String url) {
- if (logger.isDebugEnabled()) {
- logger
- .debug("handleSelfRegistrationNotification: called with login: "
- + login
- + ", toAddress"
- + toAddress
- + ", url: "
- + url);
- }
- try {
- if (toAddress != null && !"".equals(toAddress.getAddress())) {
- String subject = ITrackerResources
- .getString("itracker.email.selfreg.subject", locale);
- String msgText = ITrackerResources.getString(
- "itracker.email.selfreg.body", locale, new Object[]{login,
- url + "/login.do"});
- emailService.sendEmail(toAddress, subject, msgText);
- } else {
- throw new IllegalArgumentException(
- "To-address must be set for self registration notification.");
- }
- } catch (RuntimeException e) {
- logger.error("failed to handle self registration notification for "
- + toAddress, e);
- throw e;
- }
- }
- /**
- * Method for internal sending of a notification of specific type.
- */
- private void handleIssueNotification(Issue issue, Type type, String url) {
- if (logger.isDebugEnabled()) {
- logger.debug("handleIssueNotification: called with issue: " + issue
- + ", type: " + type + "url: " + url);
- }
- this.handleLocalizedIssueNotification(issue, type, url, null, null);
- }
- /**
- * Method for internal sending of a notification of specific type.
- */
- private void handleLocalizedIssueNotification(final Issue issue, final Type type, final String url,
- final InternetAddress[] recipients, Integer lastModifiedDays) {
- try {
- if (logger.isDebugEnabled()) {
- logger
- .debug("handleLocalizedIssueNotification: running as thread, called with issue: "
- + issue
- + ", type: "
- + type
- + "url: "
- + url
- + ", recipients: "
- + (null == recipients ? "<null>" : String
- .valueOf(Arrays.asList(recipients)))
- + ", lastModifiedDays: " + lastModifiedDays);
- }
- final Integer notModifiedSince;
- if (lastModifiedDays == null || lastModifiedDays < 0) {
- notModifiedSince = DEFAULT_ISSUE_AGE;
- } else {
- notModifiedSince = lastModifiedDays;
- }
- try {
- if (logger.isDebugEnabled()) {
- logger
- .debug("handleLocalizedIssueNotification.run: running as thread, called with issue: "
- + issue
- + ", type: "
- + type
- + "url: "
- + url
- + ", recipients: "
- + (null == recipients ? "<null>" : String
- .valueOf(Arrays.asList(recipients)))
- + ", notModifiedSince: " + notModifiedSince);
- }
- final List<Notification> notifications;
- if (issue == null) {
- logger
- .warn("handleLocalizedIssueNotification: issue was null. Notification will not be handled");
- return;
- }
- Map<InternetAddress, Locale> localeMapping;
- if (recipients == null) {
- notifications = this.getIssueNotifications(issue);
- localeMapping = new Hashtable<>(notifications.size());
- Iterator<Notification> it = notifications.iterator();
- User currentUser;
- while (it.hasNext()) {
- currentUser = it.next().getUser();
- if (null != currentUser
- && null != currentUser.getEmailAddress()
- && null != currentUser.getEmail()
- && (!localeMapping.keySet()
- .contains(currentUser.getEmailAddress()))) {
- try {
- localeMapping.put(currentUser.getEmailAddress(), ITrackerResources.getLocale(currentUser.getPreferences().getUserLocale()));
- } catch (RuntimeException re) {
- localeMapping.put(currentUser.getEmailAddress(), ITrackerResources.getLocale());
- }
- }
- }
- } else {
- localeMapping = new Hashtable<>(1);
- Locale locale = ITrackerResources.getLocale();
- for (InternetAddress internetAddress : Arrays.asList(recipients)) {
- localeMapping.put(internetAddress, locale);
- }
- }
- this.handleNotification(issue, type, localeMapping, url, notModifiedSince);
- } catch (Exception e) {
- logger.error("run: failed to process notification", e);
- }
- } catch (Exception e) {
- logger
- .error(
- "handleLocalizedIssueNotification: unexpected exception caught, throwing runtime exception",
- e);
- throw new RuntimeException(e);
- }
- }
- @Override
- public void sendReminder(Issue issue, User user, String baseURL, int issueAge) {
- Map<InternetAddress, Locale> recipient = new HashMap<>(1);
- recipient.put(user.getEmailAddress(), ITrackerResources.getLocale(user.getPreferences().getUserLocale()));
- handleNotification(issue, Type.ISSUE_REMINDER, recipient, baseURL, issueAge);
- }
- /**
- * Send notifications to mapped addresses by locale.
- */
- private void handleNotification(Issue issue, Type type, Map<InternetAddress, Locale> recipientsLocales, final String url, Integer notModifiedSince) {
- Set<InternetAddress> recipients;
- Map<Locale, Set<InternetAddress>> localeRecipients = new Hashtable<>();
- List<Component> components = issue.getComponents();
- List<IssueActivity> activity = getIssueService().getIssueActivity(
- issue.getId(), false);
- IssueHistory history;
- history = getIssueService().getLastIssueHistory(issue.getId());
- StringBuilder recipientsString = new StringBuilder();
- if (logger.isDebugEnabled() && null != history) {
- logger.debug("handleIssueNotification: got most recent history: "
- + history
- + " ("
- + history.getDescription()
- + ")");
- }
- for (InternetAddress internetAddress : recipientsLocales.keySet()) {
- recipientsString.append("\n ");
- recipientsString.append(internetAddress.getPersonal());
- if (localeRecipients.keySet().contains(recipientsLocales.get(internetAddress))) {
- localeRecipients.get(recipientsLocales.get(internetAddress)).add(internetAddress);
- } else {
- Set<InternetAddress> addresses = new HashSet<>();
- addresses.add(internetAddress);
- localeRecipients.put(recipientsLocales.get(internetAddress), addresses);
- }
- }
- Iterator<Locale> localesIt = localeRecipients.keySet().iterator();
- try {
- while (localesIt.hasNext()) {
- Locale currentLocale = localesIt.next();
- recipients = localeRecipients.get(currentLocale);
- if (recipients.size() > 0) {
- String subject;
- if (type == Type.CREATED) {
- subject = ITrackerResources.getString(
- "itracker.email.issue.subject.created",
- currentLocale,
- new Object[]{issue.getId(),
- issue.getProject().getName()});
- } else if (type == Type.ASSIGNED) {
- subject = ITrackerResources.getString(
- "itracker.email.issue.subject.assigned",
- currentLocale,
- new Object[]{issue.getId(),
- issue.getProject().getName()});
- } else if (type == Type.CLOSED) {
- subject = ITrackerResources.getString(
- "itracker.email.issue.subject.closed",
- currentLocale,
- new Object[]{issue.getId(),
- issue.getProject().getName()});
- } else if (type == Type.ISSUE_REMINDER) {
- subject = ITrackerResources.getString(
- "itracker.email.issue.subject.reminder",
- currentLocale,
- new Object[]{issue.getId(),
- issue.getProject().getName(),
- notModifiedSince});
- } else {
- subject = ITrackerResources.getString(
- "itracker.email.issue.subject.updated",
- currentLocale,
- new Object[]{issue.getId(),
- issue.getProject().getName()});
- }
- String activityString;
- String componentString = "";
- StringBuilder sb = new StringBuilder();
- if (activity.size() == 0) {
- sb.append("-");
- } else {
- for (IssueActivity anActivity : activity) {
- sb.append("\n ").append(
- IssueUtilities.getActivityName(anActivity
- .getActivityType(), currentLocale)).append(": ").append(
- anActivity.getDescription());
- }
- }
- sb.append("\n");
- activityString = sb.toString();
- for (int i = 0; i < components.size(); i++) {
- componentString += (i != 0 ? ", " : "")
- + components.get(i).getName();
- }
- final String owner = IssueUtilities.getOwnerName(issue.getOwner(), currentLocale);
- final User hUser = null == history ? null : history.getUser();
- final String historyUser = (null != hUser) ? hUser.getFullName()
- : ITrackerResources.getString("itracker.web.generic.notapplicable", currentLocale);
- final String historyText = (history == null ? "-"
- : HTMLUtilities
- .removeMarkup(history
- .getDescription()));
- final String status =
- IssueUtilities.getStatusName(issue
- .getStatus(), currentLocale);
- final String msgText;
- if (type == Type.ISSUE_REMINDER) {
- msgText = ITrackerResources
- .getString(
- "itracker.email.issue.body.reminder",
- currentLocale,
- new Object[]{
- IssueUtilities.getIssueURL(issue, url).toExternalForm(),
- issue.getProject().getName(),
- issue.getDescription(),
- IssueUtilities.getStatusName(issue
- .getStatus(), currentLocale),
- IssueUtilities
- .getSeverityName(issue
- .getSeverity(), currentLocale),
- owner,
- componentString,
- historyUser,
- historyText,
- notModifiedSince,
- activityString});
- } else {
- String resolution = (issue.getResolution() == null ? ""
- : issue.getResolution());
- if (!resolution.equals("")
- && ProjectUtilities
- .hasOption(
- ProjectUtilities.OPTION_PREDEFINED_RESOLUTIONS,
- issue.getProject().getOptions())) {
- resolution = IssueUtilities.getResolutionName(
- resolution, currentLocale);
- }
- msgText = ITrackerResources
- .getString(
- "itracker.email.issue.body."
- + (type == Type.CREATED ? "created" : "standard"),
- currentLocale,
- new Object[]{
- url + "/module-projects/view_issue.do?id=" + issue.getId(),
- issue.getProject().getName(),
- issue.getDescription(),
- status,
- resolution,
- IssueUtilities
- .getSeverityName(issue
- .getSeverity(), currentLocale),
- owner,
- componentString,
- historyUser,
- historyText,
- activityString,
- recipientsString});
- }
- if (logger.isInfoEnabled()) {
- logger.info("handleNotification: sending notification for " + issue + " (" + type + ") to " + currentLocale + "-users (" + recipients + ")");
- }
- for (InternetAddress iadr : recipients) {
- emailService.sendEmail(iadr, subject, msgText);
- }
- if (logger.isDebugEnabled()) {
- logger.debug("handleNotification: sent notification for " + issue
- + ": " + subject + "\n " + msgText);
- }
- }
- updateIssueActivityNotification(issue, true);
- if (logger.isDebugEnabled()) {
- logger.debug("handleNotification: sent notification for locales " + localeRecipients.keySet() + " recipients: " + localeRecipients.values());
- }
- }
- } catch (RuntimeException e) {
- logger.error("handleNotification: failed to notify: " + issue + " (locales: " + localeRecipients.keySet() + ")", e);
- } catch (MalformedURLException e) {
- logger.error("handleNotification: URL was not well-formed", e);
- }
- }
- private IssueService getIssueService() {
- if (null == issueService) {
- setIssueService((IssueService) applicationContext.getBean("issueService"));
- }
- return issueService;
- }
- public void setIssueService(IssueService issueService) {
- this.issueService = issueService;
- }
- public void updateIssueActivityNotification(Issue issue,
- Boolean notificationSent) {
- if (logger.isDebugEnabled()) {
- logger.debug("updateIssueActivityNotification: called with "
- + issue + ", notificationSent: " + notificationSent);
- }
- Collection<IssueActivity> activities = getIssueActivityDao()
- .findByIssueId(issue.getId());
- for (IssueActivity activity : activities) {
- activity.setNotificationSent(notificationSent);
- }
- }
- /**
- */
- public boolean addIssueNotification(Notification notification) {
- if (logger.isDebugEnabled()) {
- logger.debug("addIssueNotification: called with notification: "
- + notification);
- }
- Issue issue = notification.getIssue();
- if (!issue.getNotifications().contains(notification)) {
- if (notification.getCreateDate() == null) {
- notification.setCreateDate(new Date());
- }
- if (notification.getLastModifiedDate() == null) {
- notification.setLastModifiedDate(new Date());
- }
- getNotificationDao().save(notification);
- issue.getNotifications().add(notification);
- getIssueDao().merge(issue);
- return true;
- }
- if (logger.isDebugEnabled()) {
- logger.debug("addIssueNotification: attempted to add duplicate notification " + notification + " for issue: " + issue);
- }
- return false;
- }
- /**
- *
- */
- public List<Notification> getIssueNotifications(Issue issue,
- boolean primaryOnly, boolean activeOnly) {
- if (logger.isDebugEnabled()) {
- logger.debug("getIssueNotifications: called with issue: " + issue
- + ", primaryOnly: " + primaryOnly + ", activeOnly: "
- + activeOnly);
- }
- List<Notification> issueNotifications = new ArrayList<>();
- if (issue == null) {
- logger.warn("getIssueNotifications: no issue, throwing exception");
- throw new IllegalArgumentException("issue must not be null");
- }
- if (!primaryOnly) {
- List<Notification> notifications = getNotificationDao()
- .findByIssueId(issue.getId());
- for (Notification notification : notifications) {
- User notificationUser = notification.getUser();
- if (!activeOnly
- || notificationUser.getStatus() == UserUtilities.STATUS_ACTIVE) {
- issueNotifications.add(notification);
- }
- }
- }
- // Now add in other notifications like owner, creator, project owners,
- // etc...
- boolean hasOwner = false;
- if (issue.getOwner() != null) {
- User ownerModel = issue.getOwner();
- if (ownerModel != null
- && (!activeOnly || ownerModel.getStatus() == UserUtilities.STATUS_ACTIVE)) {
- issueNotifications.add(new Notification(ownerModel, issue,
- Role.OWNER));
- hasOwner = true;
- }
- }
- if (!primaryOnly || !hasOwner) {
- User creatorModel = issue.getCreator();
- if (creatorModel != null
- && (!activeOnly || creatorModel.getStatus() == UserUtilities.STATUS_ACTIVE)) {
- issueNotifications.add(new Notification(creatorModel,
- issue, Role.CREATOR));
- }
- }
- Project project = getProjectService().getProject(
- issue.getProject().getId());
- for (User projectOwner : project.getOwners()) {
- if (projectOwner != null
- && (!activeOnly || projectOwner.getStatus() == UserUtilities.STATUS_ACTIVE)) {
- issueNotifications.add(new Notification(projectOwner,
- issue, Role.PO));
- }
- }
- if (logger.isDebugEnabled()) {
- logger.debug("getIssueNotifications: returning "
- + issueNotifications);
- }
- return issueNotifications;
- }
- public List<Notification> getIssueNotifications(Issue issue) {
- if (logger.isDebugEnabled()) {
- logger.debug("getIssueNotifications: called with: " + issue);
- }
- return this.getIssueNotifications(issue, false, true);
- }
- public List<Notification> getPrimaryIssueNotifications(Issue issue) {
- if (logger.isDebugEnabled()) {
- logger.debug("getPrimaryIssueNotifications: called with: " + issue);
- }
- return this.getIssueNotifications(issue, true, false);
- }
- public boolean hasIssueNotification(Issue issue, Integer userId) {
- if (logger.isDebugEnabled()) {
- logger.debug("hasIssueNotification: called with: " + issue
- + ", userId: " + userId);
- }
- return hasIssueNotification(issue, userId, Role.ANY);
- }
- @Override
- public boolean hasIssueNotification(Issue issue, String login) {
- return hasIssueNotification(issue, login, Role.ANY);
- }
- @Override
- public boolean hasIssueNotification(Issue issue, String login, Role role) {
- if (issue != null && StringUtils.isNotBlank(login)) {
- List<Notification> notifications = getIssueNotifications(issue,
- false, false);
- for (Notification notification : notifications) {
- if (role == Role.ANY || notification.getRole() == role) {
- if (StringUtils.equals(login, notification.getUser().getLogin())) {
- return true;
- }
- }
- }
- }
- return false;
- }
- public boolean hasIssueNotification(Issue issue, Integer userId, Role role) {
- if (issue != null && userId != null) {
- List<Notification> notifications = getIssueNotifications(issue,
- false, false);
- for (Notification notification : notifications) {
- if (role == Role.ANY || notification.getRole() == role) {
- if (notification.getUser().getId().equals(userId)) {
- return true;
- }
- }
- }
- }
- return false;
- }
- public boolean removeIssueNotification(Integer notificationId) {
- Notification notification = this.getNotificationDao().findById(
- notificationId);
- getNotificationDao().delete(notification);
- return true;
- }
- public void sendNotification(Issue issue, Type type, String baseURL,
- InternetAddress[] receipients, Integer lastModifiedDays) {
- this.handleLocalizedIssueNotification(issue, type, baseURL, receipients,
- lastModifiedDays);
- }
- /**
- * @return the emailService
- */
- public EmailService getEmailService() {
- return emailService;
- }
- /**
- * @return the notificationDao
- */
- private NotificationDAO getNotificationDao() {
- return notificationDao;
- }
- /**
- * @return the projectService
- */
- public ProjectService getProjectService() {
- return projectService;
- }
- /**
- * @param projectService the projectService to set
- */
- public void setProjectService(ProjectService projectService) {
- this.projectService = projectService;
- }
- /**
- * @param notificationDao the notificationDao to set
- */
- public void setNotificationDao(NotificationDAO notificationDao) {
- if (null == notificationDao) {
- throw new IllegalArgumentException(
- "notification dao must not be null");
- }
- if (null != this.notificationDao) {
- throw new IllegalStateException("notification dao allready set");
- }
- this.notificationDao = notificationDao;
- }
- /**
- * @return the issueActivityDao
- */
- public IssueActivityDAO getIssueActivityDao() {
- return issueActivityDao;
- }
- /**
- * @param issueActivityDao the issueActivityDao to set
- */
- public void setIssueActivityDao(IssueActivityDAO issueActivityDao) {
- this.issueActivityDao = issueActivityDao;
- }
- /**
- * @return the issueDao
- */
- public IssueDAO getIssueDao() {
- return issueDao;
- }
- /**
- * @param issueDao the issueDao to set
- */
- public void setIssueDao(IssueDAO issueDao) {
- this.issueDao = issueDao;
- }
- public String getIssueServiceName() {
- return issueServiceName;
- }
- public void setIssueServiceName(String issueServiceName) {
- this.issueServiceName = issueServiceName;
- }
- }