001 /** 002 * Copyright 2005-2014 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.rice.ken.web.spring; 017 018 import org.apache.commons.lang.StringUtils; 019 import org.apache.log4j.Logger; 020 import org.kuali.rice.core.api.criteria.QueryByCriteria; 021 import org.kuali.rice.core.api.exception.RiceIllegalArgumentException; 022 import org.kuali.rice.core.framework.persistence.dao.GenericDao; 023 import org.kuali.rice.coreservice.api.namespace.Namespace; 024 import org.kuali.rice.coreservice.api.namespace.NamespaceService; 025 import org.kuali.rice.ken.bo.NotificationBo; 026 import org.kuali.rice.ken.bo.NotificationChannelBo; 027 import org.kuali.rice.ken.bo.NotificationChannelReviewerBo; 028 import org.kuali.rice.ken.bo.NotificationPriorityBo; 029 import org.kuali.rice.ken.bo.NotificationProducerBo; 030 import org.kuali.rice.ken.bo.NotificationRecipientBo; 031 import org.kuali.rice.ken.bo.NotificationSenderBo; 032 import org.kuali.rice.ken.document.kew.NotificationWorkflowDocument; 033 import org.kuali.rice.ken.exception.ErrorList; 034 import org.kuali.rice.ken.service.NotificationChannelService; 035 import org.kuali.rice.ken.service.NotificationMessageContentService; 036 import org.kuali.rice.ken.service.NotificationRecipientService; 037 import org.kuali.rice.ken.service.NotificationService; 038 import org.kuali.rice.ken.service.NotificationWorkflowDocumentService; 039 import org.kuali.rice.ken.util.NotificationConstants; 040 import org.kuali.rice.ken.util.Util; 041 import org.kuali.rice.kew.api.WorkflowDocument; 042 import org.kuali.rice.kew.rule.GenericAttributeContent; 043 import org.kuali.rice.kim.api.KimConstants; 044 import org.kuali.rice.kim.api.group.Group; 045 import org.kuali.rice.kim.api.group.GroupService; 046 import org.kuali.rice.kim.api.identity.IdentityService; 047 import org.kuali.rice.kim.api.identity.principal.Principal; 048 import org.kuali.rice.kim.api.services.KimApiServiceLocator; 049 import org.kuali.rice.coreservice.api.CoreServiceApiServiceLocator; 050 import org.kuali.rice.krad.data.DataObjectService; 051 import org.springframework.web.servlet.ModelAndView; 052 import org.springframework.web.servlet.mvc.multiaction.MultiActionController; 053 054 import javax.servlet.ServletException; 055 import javax.servlet.http.HttpServletRequest; 056 import java.io.IOException; 057 import java.sql.Timestamp; 058 import java.text.ParseException; 059 import java.util.ArrayList; 060 import java.util.Date; 061 import java.util.HashMap; 062 import java.util.List; 063 import java.util.Map; 064 065 /** 066 * Base class for KEN controllers for sending notifications 067 * 068 * @author Kuali Rice Team (rice.collab@kuali.org) 069 * 070 */ 071 public class BaseSendNotificationController extends MultiActionController { 072 private static final Logger LOG = Logger.getLogger(BaseSendNotificationController.class); 073 074 private static final String USER_RECIPS_PARAM = "userRecipients"; 075 private static final String WORKGROUP_RECIPS_PARAM = "workgroupRecipients"; 076 private static final String WORKGROUP_NAMESPACE_CODES_PARAM = "workgroupNamespaceCodes"; 077 private static final String SPLIT_REGEX = "(%2C|,)"; 078 079 private static final String NONE_CHANNEL = "___NONE___"; 080 private static final long REASONABLE_IMMEDIATE_TIME_THRESHOLD = 1000 * 60 * 5; // <= 5 minutes is "immediate" 081 082 private static IdentityService identityService; 083 private static GroupService groupService; 084 private static NamespaceService namespaceService; 085 086 protected NotificationService notificationService; 087 protected NotificationWorkflowDocumentService notificationWorkflowDocService; 088 protected NotificationChannelService notificationChannelService; 089 protected NotificationRecipientService notificationRecipientService; 090 protected NotificationMessageContentService notificationMessageContentService; 091 protected DataObjectService dataObjectService; 092 093 protected static IdentityService getIdentityService() { 094 if ( identityService == null ) { 095 identityService = KimApiServiceLocator.getIdentityService(); 096 } 097 return identityService; 098 } 099 100 protected static GroupService getGroupService() { 101 if ( groupService == null ) { 102 groupService = KimApiServiceLocator.getGroupService(); 103 } 104 return groupService; 105 } 106 107 protected static NamespaceService getNamespaceService() { 108 if ( namespaceService == null ) { 109 namespaceService = CoreServiceApiServiceLocator.getNamespaceService(); 110 } 111 return namespaceService; 112 } 113 114 /** 115 * Sets the {@link NotificationService}. 116 * 117 * @param notificationService the service to set 118 */ 119 public void setNotificationService(NotificationService notificationService) { 120 this.notificationService = notificationService; 121 } 122 123 /** 124 * Sets the {@link NotificationWorkflowDocumentService}. 125 * 126 * @param notificationWorkflowDocService the service to set 127 */ 128 public void setNotificationWorkflowDocumentService(NotificationWorkflowDocumentService notificationWorkflowDocService) { 129 this.notificationWorkflowDocService = notificationWorkflowDocService; 130 } 131 132 /** 133 * Sets the {@link NotificationChannelService}. 134 * 135 * @param notificationChannelService the service to set 136 */ 137 public void setNotificationChannelService(NotificationChannelService notificationChannelService) { 138 this.notificationChannelService = notificationChannelService; 139 } 140 141 /** 142 * Sets the {@link NotificationRecipientService}. 143 * 144 * @param notificationRecipientService the service to set 145 */ 146 public void setNotificationRecipientService(NotificationRecipientService notificationRecipientService) { 147 this.notificationRecipientService = notificationRecipientService; 148 } 149 150 /** 151 * Sets the {@link NotificationMessageContentService}. 152 * 153 * @param notificationMessageContentService the service to set 154 */ 155 public void setNotificationMessageContentService(NotificationMessageContentService notificationMessageContentService) { 156 this.notificationMessageContentService = notificationMessageContentService; 157 } 158 159 /** 160 * Sets the businessObjectDao attribute value. 161 * @param dataObjectService the service to set 162 */ 163 public void setDataObjectService(DataObjectService dataObjectService) { 164 this.dataObjectService = dataObjectService; 165 } 166 167 168 protected String getParameter(HttpServletRequest request, String parameterName, Map<String, Object> model, ErrorList errors, String errorMessage) { 169 String parameter = request.getParameter(parameterName); 170 171 if (StringUtils.isNotEmpty(parameter)) { 172 model.put(parameterName, parameter); 173 } else { 174 errors.addError(errorMessage); 175 } 176 177 return parameter; 178 } 179 180 protected String getParameter(HttpServletRequest request, String parameterName, Map<String, Object> model, ErrorList errors, String errorMessage, String defaultValue) { 181 String parameter = StringUtils.defaultIfBlank(request.getParameter(parameterName), defaultValue); 182 183 if (StringUtils.isNotEmpty(parameter)) { 184 model.put(parameterName, parameter); 185 } else { 186 errors.addError(errorMessage); 187 } 188 189 return parameter; 190 } 191 192 protected String[] getParameterList(HttpServletRequest request, String parameterName, Map<String, Object> model, ErrorList errors, String errorMessage) { 193 String parameter = request.getParameter(parameterName); 194 String[] senders = null; 195 196 if (StringUtils.isNotEmpty(parameter)) { 197 senders = StringUtils.split(parameter, ","); 198 model.put(parameterName, parameter); 199 } else { 200 errors.addError(errorMessage); 201 } 202 203 return senders; 204 } 205 206 protected Date getDate(String parameter, ErrorList errors, String errorMessage) { 207 Date date = null; 208 209 try { 210 date = Util.parseUIDateTime(parameter); 211 } catch (ParseException pe) { 212 errors.addError(errorMessage); 213 } 214 215 return date; 216 } 217 218 protected String[] parseUserRecipients(HttpServletRequest request) { 219 return parseCommaSeparatedValues(request, USER_RECIPS_PARAM); 220 } 221 222 protected String[] parseWorkgroupRecipients(HttpServletRequest request) { 223 return parseCommaSeparatedValues(request, WORKGROUP_RECIPS_PARAM); 224 } 225 226 protected String[] parseWorkgroupNamespaceCodes(HttpServletRequest request) { 227 return parseCommaSeparatedValues(request, WORKGROUP_NAMESPACE_CODES_PARAM); 228 } 229 230 protected String[] parseCommaSeparatedValues(HttpServletRequest request, String param) { 231 String vals = request.getParameter(param); 232 if (vals != null) { 233 String[] split = vals.split(SPLIT_REGEX); 234 List<String> strs = new ArrayList<String>(); 235 for (String component: split) { 236 if (StringUtils.isNotBlank(component)) { 237 strs.add(component.trim()); 238 } 239 } 240 return strs.toArray(new String[strs.size()]); 241 } else { 242 return new String[0]; 243 } 244 } 245 246 protected boolean isUserRecipientValid(String user, ErrorList errors) { 247 boolean valid = true; 248 Principal principal = getIdentityService().getPrincipalByPrincipalName(user); 249 if (principal == null) { 250 valid = false; 251 errors.addError("'" + user + "' is not a valid principal name"); 252 } 253 254 return valid; 255 } 256 257 protected boolean isWorkgroupRecipientValid(String groupName, String namespaceCode, ErrorList errors) { 258 Namespace nSpace = getNamespaceService().getNamespace(namespaceCode); 259 if (nSpace == null) { 260 errors.addError((new StringBuilder()).append('\'').append(namespaceCode).append("' is not a valid namespace code").toString()); 261 return false; 262 } else { 263 Group i = getGroupService().getGroupByNamespaceCodeAndName(namespaceCode, groupName); 264 if (i == null) { 265 errors.addError((new StringBuilder()).append('\'').append(groupName).append( 266 "' is not a valid group name for namespace code '").append(namespaceCode).append('\'').toString()); 267 return false; 268 } else { 269 return true; 270 } 271 } 272 } 273 protected String getPrincipalIdFromIdOrName(String principalIdOrName) { 274 Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(principalIdOrName); 275 if (principal == null) { 276 principal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(principalIdOrName); 277 } 278 if (principal == null) { 279 throw new RiceIllegalArgumentException("Could not locate a principal as initiator with the given remoteUser of " + principalIdOrName); 280 } 281 return principal.getPrincipalId(); 282 } 283 284 /** 285 * Submits the actual event notification message. 286 * 287 * @param request the servlet request 288 * @param routeMessage the message to attach to the route action 289 * @param viewName the name of the view to forward to after completion 290 * 291 * @return the next view to show 292 * @throws javax.servlet.ServletException 293 * @throws java.io.IOException 294 */ 295 protected ModelAndView submitNotificationMessage(HttpServletRequest request, String routeMessage, String viewName) 296 throws ServletException, IOException { 297 LOG.debug("remoteUser: " + request.getRemoteUser()); 298 299 // obtain a workflow user object first 300 //WorkflowIdDTO initiator = new WorkflowIdDTO(request.getRemoteUser()); 301 String initiatorId = getPrincipalIdFromIdOrName( request.getRemoteUser()); 302 LOG.debug("initiatorId: " + initiatorId); 303 304 // now construct the workflow document, which will interact with workflow 305 Map<String, Object> model = new HashMap<String, Object>(); 306 307 try { 308 WorkflowDocument document = createNotificationWorkflowDocument(request, initiatorId, model); 309 310 document.route(routeMessage + initiatorId); 311 312 // This ain't pretty, but it gets the job done for now. 313 ErrorList el = new ErrorList(); 314 el.addError("Notification(s) sent."); 315 model.put("errors", el); 316 } catch (ErrorList el) { 317 // route back to the send form again 318 Map<String, Object> model2 = setupModelForSendNotification(request); 319 model.putAll(model2); 320 model.put("errors", el); 321 } catch (Exception e) { 322 throw new RuntimeException(e); 323 } 324 325 return new ModelAndView(viewName, model); 326 } 327 328 /** 329 * Creates a notification {@link WorkflowDocument}. 330 * 331 * @param request the servlet request 332 * @param initiatorId the user sending the notification 333 * @param model the Spring MVC model 334 * 335 * @return a {@link WorkflowDocument} for the notification 336 * @throws java.lang.IllegalArgumentException 337 * @throws org.kuali.rice.ken.exception.ErrorList 338 */ 339 protected WorkflowDocument createNotificationWorkflowDocument(HttpServletRequest request, String initiatorId, 340 Map<String, Object> model) throws IllegalArgumentException, ErrorList { 341 WorkflowDocument document = NotificationWorkflowDocument.createNotificationDocument(initiatorId, 342 NotificationConstants.KEW_CONSTANTS.SEND_NOTIFICATION_REQ_DOC_TYPE); 343 344 //parse out the application content into a Notification BO 345 NotificationBo notification = populateNotificationInstance(request, model); 346 347 // now get that content in an understandable XML format and pass into document 348 String notificationAsXml = notificationMessageContentService.generateNotificationMessage(notification); 349 350 Map<String, String> attrFields = new HashMap<String,String>(); 351 List<NotificationChannelReviewerBo> reviewers = notification.getChannel().getReviewers(); 352 int ui = 0; 353 int gi = 0; 354 for (NotificationChannelReviewerBo reviewer: reviewers) { 355 String prefix; 356 int index; 357 if (KimConstants.KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE.getCode().equals(reviewer.getReviewerType())) { 358 prefix = "user"; 359 index = ui; 360 ui++; 361 } else if (KimConstants.KimGroupMemberTypes.GROUP_MEMBER_TYPE.getCode().equals(reviewer.getReviewerType())) { 362 prefix = "group"; 363 index = gi; 364 gi++; 365 } else { 366 LOG.error("Invalid type for reviewer " + reviewer.getReviewerId() + ": " + reviewer.getReviewerType()); 367 continue; 368 } 369 attrFields.put(prefix + index, reviewer.getReviewerId()); 370 } 371 GenericAttributeContent gac = new GenericAttributeContent("channelReviewers"); 372 document.setApplicationContent(notificationAsXml); 373 document.setAttributeContent("<attributeContent>" + gac.generateContent(attrFields) + "</attributeContent>"); 374 375 document.setTitle(notification.getTitle()); 376 377 return document; 378 } 379 380 /** 381 * Creates a new {@link NotificationBo} instance. 382 * 383 * @param request the servlet request 384 * @param model the Spring MVC model 385 * 386 * @return a new notification 387 * @throws java.lang.IllegalArgumentException 388 * @throws org.kuali.rice.ken.exception.ErrorList 389 */ 390 protected NotificationBo populateNotificationInstance(HttpServletRequest request, Map<String, Object> model) 391 throws IllegalArgumentException, ErrorList { 392 return createNotification(request, model, new ErrorList()); 393 } 394 395 /** 396 * Provides an overridable method in which to customize a created {@link NotificationBo} instance. 397 * 398 * @param request the servlet request 399 * @param model the Spring MVC model 400 * @param errors the error list 401 * 402 * @return a new notification 403 * @throws ErrorList 404 */ 405 protected NotificationBo createNotification(HttpServletRequest request, Map<String, Object> model, ErrorList errors) 406 throws ErrorList { 407 String channelName = getChannelName(request, model, errors); 408 String priorityName = getParameter(request, "priorityName", model, errors, "You must choose a priority."); 409 String[] senders = getParameterList(request, "senderNames", model, errors, "You must enter at least one sender."); 410 String deliveryType = getDeliveryType(request, model, errors); 411 412 Date originalDate = getDate(request.getParameter("originalDateTime"), errors, "Original date is invalid."); 413 414 String sendDateTime = StringUtils.defaultIfBlank(request.getParameter("sendDateTime"), Util.getCurrentDateTime()); 415 Date sendDate = getDate(sendDateTime, errors, "You specified an invalid Send Date/Time. Please use the calendar picker."); 416 if (sendDate != null && sendDate.before(originalDate)) { 417 errors.addError("Send Date/Time cannot be in the past."); 418 } 419 model.put("sendDateTime", sendDateTime); 420 421 String autoRemoveDateTime = request.getParameter("autoRemoveDateTime"); 422 Date removeDate = getDate(autoRemoveDateTime, errors, "You specified an invalid Auto-Remove Date/Time. Please use the calendar picker."); 423 if (removeDate != null) { 424 if (removeDate.before(originalDate)) { 425 errors.addError("Auto-Remove Date/Time cannot be in the past."); 426 } else if (sendDate != null && removeDate.before(sendDate)) { 427 errors.addError("Auto-Remove Date/Time cannot be before the Send Date/Time."); 428 } 429 } 430 model.put("autoRemoveDateTime", autoRemoveDateTime); 431 432 // user recipient names 433 String[] userRecipients = parseUserRecipients(request); 434 435 // workgroup recipient names 436 String[] workgroupRecipients = parseWorkgroupRecipients(request); 437 438 // workgroup namespace codes 439 String[] workgroupNamespaceCodes = parseWorkgroupNamespaceCodes(request); 440 441 String title = getParameter(request, "title", model, errors, "You must fill in a title."); 442 443 // check to see if there were any errors 444 if (!errors.getErrors().isEmpty()) { 445 throw errors; 446 } 447 448 return createNotification(title, deliveryType, sendDate, removeDate, channelName, priorityName, 449 senders, userRecipients, workgroupRecipients, workgroupNamespaceCodes, errors); 450 } 451 452 private NotificationBo createNotification(String title, String deliveryType, Date sendDate, Date removeDate, 453 String channelName, String priorityName, String[] senders, String[] userRecipients, 454 String[] workgroupRecipients, String[] workgroupNamespaceCodes, ErrorList errors) throws ErrorList { 455 NotificationBo notification = new NotificationBo(); 456 notification.setTitle(title); 457 notification.setDeliveryType(deliveryType); 458 notification.setSendDateTimeValue(new Timestamp(sendDate.getTime())); 459 notification.setAutoRemoveDateTimeValue(new Timestamp(removeDate.getTime())); 460 461 NotificationChannelBo channel = Util.retrieveFieldReference("channel", "name", channelName, 462 NotificationChannelBo.class, dataObjectService); 463 notification.setChannel(channel); 464 465 NotificationPriorityBo priority = Util.retrieveFieldReference("priority", "name", priorityName, 466 NotificationPriorityBo.class, dataObjectService); 467 notification.setPriority(priority); 468 469 NotificationProducerBo producer = Util.retrieveFieldReference("producer", "name", 470 NotificationConstants.KEW_CONSTANTS.NOTIFICATION_SYSTEM_USER_NAME, NotificationProducerBo.class, 471 dataObjectService); 472 notification.setProducer(producer); 473 474 for (String senderName : senders) { 475 if (StringUtils.isEmpty(senderName)) { 476 errors.addError("A sender's name cannot be blank."); 477 } else { 478 NotificationSenderBo ns = new NotificationSenderBo(); 479 ns.setSenderName(senderName.trim()); 480 notification.addSender(ns); 481 } 482 } 483 484 if (userRecipients != null && userRecipients.length > 0) { 485 for (String userRecipientId : userRecipients) { 486 if (isUserRecipientValid(userRecipientId, errors)) { 487 NotificationRecipientBo recipient = new NotificationRecipientBo(); 488 recipient.setRecipientType(KimConstants.KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE.getCode()); 489 recipient.setRecipientId(userRecipientId); 490 notification.addRecipient(recipient); 491 } 492 } 493 } 494 495 if (workgroupRecipients != null && workgroupRecipients.length > 0) { 496 if (workgroupNamespaceCodes != null && workgroupNamespaceCodes.length > 0) { 497 if (workgroupNamespaceCodes.length == workgroupRecipients.length) { 498 for (int i = 0; i < workgroupRecipients.length; i++) { 499 if (isWorkgroupRecipientValid(workgroupRecipients[i], workgroupNamespaceCodes[i], errors)) { 500 NotificationRecipientBo recipient = new NotificationRecipientBo(); 501 recipient.setRecipientType(KimConstants.KimGroupMemberTypes.GROUP_MEMBER_TYPE.getCode()); 502 recipient.setRecipientId( 503 getGroupService().getGroupByNamespaceCodeAndName(workgroupNamespaceCodes[i], 504 workgroupRecipients[i]).getId()); 505 notification.addRecipient(recipient); 506 } 507 } 508 } else { 509 errors.addError("The number of groups must match the number of namespace codes"); 510 } 511 } else { 512 errors.addError("You must specify a namespace code for every group name"); 513 } 514 } else if (workgroupNamespaceCodes != null && workgroupNamespaceCodes.length > 0) { 515 errors.addError("You must specify a group name for every namespace code"); 516 } 517 518 if (!recipientsExist(userRecipients, workgroupRecipients) && !hasPotentialRecipients(notification)) { 519 errors.addError("You must specify at least one user or group recipient."); 520 } 521 522 notification.setContent(NotificationConstants.XML_MESSAGE_CONSTANTS.CONTENT_SIMPLE_OPEN 523 + NotificationConstants.XML_MESSAGE_CONSTANTS.MESSAGE_OPEN 524 + NotificationConstants.XML_MESSAGE_CONSTANTS.MESSAGE_CLOSE 525 + NotificationConstants.XML_MESSAGE_CONSTANTS.CONTENT_CLOSE); 526 527 return notification; 528 } 529 530 private String getChannelName(HttpServletRequest request, Map<String, Object> model, ErrorList errors) { 531 String channelName = request.getParameter("channelName"); 532 533 if (StringUtils.isEmpty(channelName) || StringUtils.equals(channelName, NONE_CHANNEL)) { 534 errors.addError("You must choose a channel."); 535 } else { 536 model.put("channelName", channelName); 537 } 538 539 return channelName; 540 } 541 542 private String getDeliveryType(HttpServletRequest request, Map<String, Object> model, ErrorList errors) { 543 String deliveryType = request.getParameter("deliveryType"); 544 545 if (StringUtils.isNotEmpty(deliveryType)) { 546 if (deliveryType.equalsIgnoreCase(NotificationConstants.DELIVERY_TYPES.FYI)) { 547 deliveryType = NotificationConstants.DELIVERY_TYPES.FYI; 548 } else { 549 deliveryType = NotificationConstants.DELIVERY_TYPES.ACK; 550 } 551 model.put("deliveryType", deliveryType); 552 } else { 553 errors.addError("You must choose a delivery type."); 554 } 555 556 return deliveryType; 557 } 558 559 /** 560 * Prepares the model used for sending the notification. 561 * 562 * @param request the servlet request 563 * 564 * @return the Spring MVC model 565 */ 566 protected Map<String, Object> setupModelForSendNotification(HttpServletRequest request) { 567 Map<String, Object> model = new HashMap<String, Object>(); 568 569 model.put("defaultSender", request.getRemoteUser()); 570 model.put("channels", notificationChannelService.getAllNotificationChannels()); 571 model.put("priorities", dataObjectService.findMatching(NotificationPriorityBo.class, 572 QueryByCriteria.Builder.create().build()).getResults()); 573 574 // set sendDateTime to current datetime if not provided 575 String sendDateTime = request.getParameter("sendDateTime"); 576 String currentDateTime = Util.getCurrentDateTime(); 577 if (StringUtils.isEmpty(sendDateTime)) { 578 sendDateTime = currentDateTime; 579 } 580 model.put("sendDateTime", sendDateTime); 581 582 // retain the original date time or set to current if it was not in the request 583 if (request.getParameter("originalDateTime") == null) { 584 model.put("originalDateTime", currentDateTime); 585 } else { 586 model.put("originalDateTime", request.getParameter("originalDateTime")); 587 } 588 589 model.put("userRecipients", request.getParameter("userRecipients")); 590 model.put("workgroupRecipients", request.getParameter("workgroupRecipients")); 591 model.put("workgroupNamespaceCodes", request.getParameter("workgroupNamespaceCodes")); 592 593 return model; 594 } 595 596 /** 597 * Returns whether the specified time is considered "in the future", based on some reasonable threshold. 598 * 599 * @param time the time to test 600 * 601 * @return true if the specified time is considered "in the future", false otherwise 602 */ 603 private boolean timeIsInTheFuture(long time) { 604 boolean future = (time - System.currentTimeMillis()) > REASONABLE_IMMEDIATE_TIME_THRESHOLD; 605 LOG.info("Time: " + new Date(time) + " is in the future? " + future); 606 return future; 607 } 608 609 /** 610 * Returns whether recipients exist either, from users or workgroups. 611 * 612 * @param userRecipients the list of user recipients 613 * @param workgroupRecipients the list of workgroup recipients 614 * 615 * @return true if there are any recipients, false otherwise 616 */ 617 private boolean recipientsExist(String[] userRecipients, String[] workgroupRecipients) { 618 return (userRecipients != null && userRecipients.length > 0) 619 || (workgroupRecipients != null && workgroupRecipients.length > 0); 620 } 621 622 /** 623 * Returns whether the specified Notification can be reasonably expected to have recipients. 624 * 625 * This is determined on whether the channel has default recipients, is subscribable, and whether the send date time 626 * is far enough in the future to expect that if there are no subscribers, there may actually be some by the time 627 * the notification is sent. 628 * @param notification the notification to test 629 * 630 * @return whether the specified Notification can be reasonably expected to have recipients 631 */ 632 private boolean hasPotentialRecipients(NotificationBo notification) { 633 LOG.info("notification channel " + notification.getChannel() + " is subscribable: " + notification.getChannel().isSubscribable()); 634 return !notification.getChannel().getRecipientLists().isEmpty() || 635 !notification.getChannel().getSubscriptions().isEmpty() || 636 (notification.getChannel().isSubscribable() && timeIsInTheFuture(notification.getSendDateTimeValue().getTime())); 637 } 638 }