Coverage Report - org.kuali.rice.ken.service.impl.NotificationServiceImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
NotificationServiceImpl
0%
0/90
0%
0/34
3.667
 
 1  
 /*
 2  
  * Copyright 2006-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 
 17  
 package org.kuali.rice.ken.service.impl;
 18  
 
 19  
 import org.kuali.rice.core.api.util.xml.XmlException;
 20  
 import org.kuali.rice.core.framework.persistence.dao.GenericDao;
 21  
 import org.kuali.rice.ken.bo.Notification;
 22  
 import org.kuali.rice.ken.bo.NotificationMessageDelivery;
 23  
 import org.kuali.rice.ken.bo.NotificationRecipient;
 24  
 import org.kuali.rice.ken.bo.NotificationResponse;
 25  
 import org.kuali.rice.ken.dao.NotificationDao;
 26  
 import org.kuali.rice.ken.deliverer.impl.KEWActionListMessageDeliverer;
 27  
 import org.kuali.rice.ken.service.NotificationAuthorizationService;
 28  
 import org.kuali.rice.ken.service.NotificationMessageContentService;
 29  
 import org.kuali.rice.ken.service.NotificationMessageDeliveryService;
 30  
 import org.kuali.rice.ken.service.NotificationRecipientService;
 31  
 import org.kuali.rice.ken.service.NotificationService;
 32  
 import org.kuali.rice.ken.service.NotificationWorkflowDocumentService;
 33  
 import org.kuali.rice.ken.util.NotificationConstants;
 34  
 
 35  
 import java.io.IOException;
 36  
 import java.sql.Timestamp;
 37  
 import java.util.Collection;
 38  
 import java.util.HashMap;
 39  
 
 40  
 //import org.kuali.rice.core.jpa.criteria.Criteria;
 41  
 
 42  
 /**
 43  
  * NotificationService implementation - this is the default out-of-the-box implementation of the service.
 44  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 45  
  */
 46  
 public class NotificationServiceImpl implements NotificationService {
 47  0
         private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
 48  
         .getLogger(NotificationServiceImpl.class);
 49  
 
 50  
         private GenericDao businessObjectDao;
 51  
         private NotificationDao notDao;
 52  
         private NotificationMessageContentService messageContentService;
 53  
         private NotificationAuthorizationService notificationAuthorizationService;
 54  
         private NotificationRecipientService notificationRecipientService;
 55  
         private NotificationWorkflowDocumentService notificationWorkflowDocumentService;
 56  
         private NotificationMessageDeliveryService notificationMessageDeliveryService;
 57  
 
 58  
         /**
 59  
          * Constructs a NotificationServiceImpl class instance.
 60  
          * @param businessObjectDao
 61  
          * @param messageContentService
 62  
          * @param notificationAuthorizationService
 63  
          * @param notificationRecipientService
 64  
          * @param notificationWorkflowDocumentService
 65  
          * @param notificationMessageDeliveryService
 66  
          */
 67  
         public NotificationServiceImpl(GenericDao businessObjectDao, NotificationMessageContentService messageContentService,
 68  
                         NotificationAuthorizationService notificationAuthorizationService, NotificationRecipientService notificationRecipientService, 
 69  
                         NotificationWorkflowDocumentService notificationWorkflowDocumentService, 
 70  
                         NotificationMessageDeliveryService notificationMessageDeliveryService,
 71  0
                         NotificationDao notDao) {
 72  0
                 this.businessObjectDao = businessObjectDao;
 73  0
                 this.messageContentService = messageContentService;
 74  0
                 this.notificationAuthorizationService = notificationAuthorizationService;
 75  0
                 this.notificationRecipientService = notificationRecipientService;
 76  0
                 this.notificationWorkflowDocumentService = notificationWorkflowDocumentService;
 77  0
                 this.notificationMessageDeliveryService = notificationMessageDeliveryService;
 78  0
                 this.notDao = notDao;
 79  0
         }
 80  
 
 81  
         /**
 82  
          * This is the default implementation that uses the businessObjectDao.
 83  
          * @see org.kuali.rice.ken.service.NotificationService#getNotification(java.lang.Long)
 84  
          */
 85  
         public Notification getNotification(Long id) {
 86  0
                 HashMap<String, Long> primaryKeys = new HashMap<String, Long>();
 87  0
                 primaryKeys.put(NotificationConstants.BO_PROPERTY_NAMES.ID, id);
 88  
 
 89  0
                 return (Notification) businessObjectDao.findByPrimaryKey(Notification.class, primaryKeys);
 90  
         }
 91  
 
 92  
         /**
 93  
          * This method is responsible for parsing out the notification message which is sent in as a String 
 94  
          * of XML.  It calls the appropriate services to validate the message content, converts it to a BO, 
 95  
          * and then passes it to another service where its content and meta-data is validated and if successful, it 
 96  
          * is saved.
 97  
          * @see org.kuali.rice.ken.service.NotificationService#sendNotification(java.lang.String)
 98  
          */
 99  
         public NotificationResponse sendNotification(String notificationMessageAsXml) throws IOException, XmlException {
 100  
                 // try to parse out the XML with the message content service
 101  0
                 Notification notification = messageContentService.parseNotificationRequestMessage(notificationMessageAsXml);
 102  
 
 103  
                 // now call out to the meat of the notification sending - this will validate users, groups, producers, and save
 104  0
                 return sendNotification(notification);
 105  
         }
 106  
 
 107  
         /**
 108  
          * @see org.kuali.rice.ken.service.NotificationService#sendNotification(org.kuali.rice.ken.bo.Notification)
 109  
          */
 110  
         public NotificationResponse sendNotification(Notification notification) {
 111  0
                 NotificationResponse response = new NotificationResponse();
 112  
 
 113  
                 // make sure that the producer is able to send notifications on behalf of the channel
 114  0
                 boolean producerAuthorizedForChannel = notificationAuthorizationService.isProducerAuthorizedToSendNotificationForChannel(notification.getProducer(), notification.getChannel());
 115  0
                 if(!producerAuthorizedForChannel) {
 116  0
                         LOG.error("Producer " + notification.getProducer() + " is not authorized to send messages to channel " + notification.getChannel());
 117  0
                         response.setStatus(NotificationConstants.RESPONSE_STATUSES.FAILURE);
 118  0
                         response.setMessage(NotificationConstants.RESPONSE_MESSAGES.PRODUCER_NOT_AUTHORIZED_FOR_CHANNEL);
 119  0
                         return response;
 120  
                 }
 121  
 
 122  
                 // make sure that the recipients are valid
 123  0
                 for(int i = 0; i < notification.getRecipients().size(); i++) {
 124  0
                         NotificationRecipient recipient = notification.getRecipient(i);
 125  0
                         boolean validRecipient = notificationRecipientService.isRecipientValid(recipient.getRecipientId(), recipient.getRecipientType());
 126  0
                         if(!validRecipient) {
 127  0
                                 response.setStatus(NotificationConstants.RESPONSE_STATUSES.FAILURE);
 128  0
                                 response.setMessage(NotificationConstants.RESPONSE_MESSAGES.INVALID_RECIPIENT + " - recipientId=" + 
 129  
                                                 recipient.getRecipientId() + ", recipientType=" + recipient.getRecipientType());
 130  0
                                 return response;
 131  
                         }
 132  
                 }
 133  
 
 134  
                 // set the creationDateTime attribute to the current timestamp if it's currently null
 135  0
                 if (notification.getCreationDateTime() == null) {
 136  0
                         notification.setCreationDateTime(new Timestamp(System.currentTimeMillis()));
 137  
                 }
 138  
 
 139  
                 // set the sendDateTime attribute to the current timestamp if it's currently null
 140  0
                 if(notification.getSendDateTime() == null) {
 141  0
                         notification.setSendDateTime(new Timestamp(System.currentTimeMillis()));
 142  
                 }
 143  
 
 144  
                 // if the autoremove time is before the send date time, reject the notification
 145  0
                 if (notification.getAutoRemoveDateTime() != null) {
 146  0
                         if (notification.getAutoRemoveDateTime().before(notification.getSendDateTime()))  {
 147  0
                                 response.setStatus(NotificationConstants.RESPONSE_STATUSES.FAILURE);
 148  0
                                 response.setMessage(NotificationConstants.RESPONSE_MESSAGES.INVALID_REMOVE_DATE);
 149  0
                                 return response;
 150  
                         }
 151  
                 }
 152  
 
 153  
                 // make sure the delivery types are valid
 154  0
                 if(!notification.getDeliveryType().equalsIgnoreCase(NotificationConstants.DELIVERY_TYPES.ACK) && 
 155  
                                 !notification.getDeliveryType().equalsIgnoreCase(NotificationConstants.DELIVERY_TYPES.FYI)) {
 156  0
                         response.setStatus(NotificationConstants.RESPONSE_STATUSES.FAILURE);
 157  0
                         response.setMessage(NotificationConstants.RESPONSE_MESSAGES.INVALID_DELIVERY_TYPE + " - deliveryType=" + 
 158  
                                         notification.getDeliveryType());
 159  0
                         return response;
 160  
                 }
 161  
 
 162  
                 // now try to persist the object
 163  
                 try {
 164  0
                         businessObjectDao.save(notification);
 165  0
                 } catch(Exception e) {
 166  0
                         response.setStatus(NotificationConstants.RESPONSE_STATUSES.FAILURE);
 167  0
                         response.setMessage(NotificationConstants.RESPONSE_MESSAGES.ERROR_SAVING_NOTIFICATION);
 168  0
                         return response;
 169  0
                 }
 170  
 
 171  
                 // everything looks good!
 172  0
                 response.setMessage(NotificationConstants.RESPONSE_MESSAGES.SUCCESSFULLY_RECEIVED);
 173  0
                 response.setNotificationId(notification.getId());
 174  0
                 return response;
 175  
         }
 176  
 
 177  
         /**
 178  
          * This is the default implementation that uses the businessObjectDao and its findMatching method.
 179  
          * @see org.kuali.rice.ken.service.NotificationService#getNotificationsForRecipientByType(java.lang.String, java.lang.String)
 180  
          */
 181  
         public Collection getNotificationsForRecipientByType(String contentTypeName, String recipientId) {
 182  0
                 HashMap<String, String> queryCriteria = new HashMap<String, String>();
 183  0
                 queryCriteria.put(NotificationConstants.BO_PROPERTY_NAMES.CONTENT_TYPE_NAME, contentTypeName);
 184  0
                 queryCriteria.put(NotificationConstants.BO_PROPERTY_NAMES.RECIPIENTS_RECIPIENT_ID, recipientId);
 185  
 
 186  0
                 return businessObjectDao.findMatching(Notification.class, queryCriteria);
 187  
         }
 188  
 
 189  
         /**
 190  
          * @see org.kuali.rice.ken.service.NotificationService#dismissNotificationMessageDelivery(java.lang.Long, java.lang.String)
 191  
          */
 192  
         public void dismissNotificationMessageDelivery(Long id, String user, String cause) {
 193  
                 // TODO: implement pessimistic locking on the message delivery
 194  0
                 NotificationMessageDelivery nmd = notificationMessageDeliveryService.getNotificationMessageDelivery(id);
 195  0
                 dismissNotificationMessageDelivery(nmd, user, cause);
 196  0
         }
 197  
 
 198  
         /**
 199  
          * @see org.kuali.rice.ken.service.NotificationService#dismissNotificationMessageDelivery(org.kuali.rice.ken.bo.NotificationMessageDelivery, java.lang.String, java.lang.String)
 200  
          */   
 201  
         public void dismissNotificationMessageDelivery(NotificationMessageDelivery nmd, String user, String cause) {
 202  
                 // get the notification that generated this particular message delivery
 203  0
                 Notification notification = nmd.getNotification();
 204  
 
 205  
                 // get all of the other deliveries of this notification for the user
 206  0
                 Collection<NotificationMessageDelivery> userDeliveries = notificationMessageDeliveryService.getNotificationMessageDeliveries(notification, nmd.getUserRecipientId());
 207  
 
 208  
                 final String targetStatus;
 209  
                 // if the cause was our internal "autoremove" cause, then we need to indicate
 210  
                 // the message was autoremoved instead of normally dismissed
 211  0
                 if (NotificationConstants.AUTO_REMOVE_CAUSE.equals(cause)) {
 212  0
                         targetStatus = NotificationConstants.MESSAGE_DELIVERY_STATUS.AUTO_REMOVED;
 213  
                 } else {
 214  0
                         targetStatus = NotificationConstants.MESSAGE_DELIVERY_STATUS.REMOVED;
 215  
                 }
 216  
 
 217  0
                 KEWActionListMessageDeliverer deliverer = new KEWActionListMessageDeliverer();
 218  
                 // TODO: implement pessimistic locking on all these message deliveries
 219  
                 // now, do dispatch in reverse...dismiss each message delivery via the appropriate deliverer
 220  0
                 for (NotificationMessageDelivery messageDelivery: userDeliveries) {
 221  
 
 222  
                         // don't attempt to dismiss undelivered message deliveries
 223  0
                         if (!NotificationConstants.MESSAGE_DELIVERY_STATUS.DELIVERED.equals(messageDelivery.getMessageDeliveryStatus())) {
 224  0
                                 LOG.info("Skipping dismissal of non-delivered message delivery #" + messageDelivery.getId());
 225  0
                         } else if (targetStatus.equals(messageDelivery.getMessageDeliveryStatus())) {
 226  0
                                 LOG.info("Skipping dismissal of already removed message delivery #" + messageDelivery.getId());
 227  
                         } else {
 228  0
                                 LOG.debug("Dismissing message delivery #" + messageDelivery.getId() + " " + messageDelivery.getVersionNumber());//.getLockVerNbr());
 229  
 
 230  
                                 // we have our message deliverer, so tell it to dismiss the message
 231  
                                 //try {
 232  0
                                 deliverer.dismissMessageDelivery(messageDelivery, user, cause);
 233  
                                 //} catch (NotificationMessageDismissalException nmde) {
 234  
                                 //LOG.error("Error dismissing message " + messageDelivery, nmde);
 235  
                                 //throw new RuntimeException(nmde);
 236  
                                 //}
 237  
                         }
 238  
 
 239  
                         // by definition we have succeeded at this point if no exception was thrown by the messageDeliverer
 240  
                         // so update the status of the delivery message instance to indicate its dismissal
 241  
                         // if the message delivery was not actually delivered in the first place, we still need to mark it as
 242  
                         // removed here so delivery is not attempted again
 243  0
                         messageDelivery.setMessageDeliveryStatus(targetStatus);
 244  
                         // TODO: locking
 245  
                         // mark as unlocked
 246  
                         //messageDelivery.setLockedDate(null);
 247  0
                         LOG.debug("Saving message delivery #" + messageDelivery.getId() + " " + messageDelivery.getVersionNumber());
 248  0
                         businessObjectDao.save(messageDelivery);
 249  
 
 250  0
                         LOG.debug("Message delivery '" + messageDelivery.getId() + "' for notification '" + messageDelivery.getNotification().getId() + "' was successfully dismissed.");
 251  
                 }
 252  0
         }
 253  
 
 254  
         /**
 255  
          * This method is responsible for atomically finding all untaken, unresolved notifications that are ready to be sent,
 256  
          * marking them as taken and returning them to the caller for processing.
 257  
          * NOTE: it is important that this method execute in a SEPARATE dedicated transaction; either the caller should
 258  
          * NOT be wrapped by Spring declarative transaction and this service should be wrapped (which is the case), or
 259  
          * the caller should arrange to invoke this from within a newly created transaction).
 260  
          * @return a list of available notifications that have been marked as taken by the caller
 261  
          */
 262  
         //switch to JPA criteria
 263  
         public Collection<Notification> takeNotificationsForResolution() {
 264  
                 // get all unprocessed notifications with sendDateTime <= current
 265  
 //                Criteria criteria = new Criteria();
 266  
 //                criteria.addEqualTo(NotificationConstants.BO_PROPERTY_NAMES.PROCESSING_FLAG, NotificationConstants.PROCESSING_FLAGS.UNRESOLVED);
 267  
 //                criteria.addLessOrEqualThan(NotificationConstants.BO_PROPERTY_NAMES.SEND_DATE_TIME, new Timestamp(System.currentTimeMillis()));
 268  
 //                criteria.addIsNull(NotificationConstants.BO_PROPERTY_NAMES.LOCKED_DATE);
 269  
                 //criteria = Util.makeSelectForUpdate(criteria);
 270  
 
 271  
                 //                Criteria criteria = new Criteria(Notification.class.getName());
 272  
                 //                criteria.eq(NotificationConstants.BO_PROPERTY_NAMES.PROCESSING_FLAG, NotificationConstants.PROCESSING_FLAGS.UNRESOLVED);
 273  
                 //                criteria.lte(NotificationConstants.BO_PROPERTY_NAMES.SEND_DATE_TIME, new Timestamp(System.currentTimeMillis()));
 274  
                 //                criteria.isNull(NotificationConstants.BO_PROPERTY_NAMES.LOCKED_DATE);
 275  
 
 276  
                 //Collection<Notification> available_notifications = businessObjectDao.findMatching(Notification.class, criteria, true, RiceConstants.NO_WAIT);
 277  
                 
 278  0
                 Collection<Notification> available_notifications = notDao.findMatchedNotificationsForResolution(new Timestamp(System.currentTimeMillis()), businessObjectDao);
 279  
 
 280  
                 //LOG.debug("Available notifications: " + available_notifications.size());
 281  
 
 282  
                 // mark as "taken"
 283  0
                 if (available_notifications != null) {
 284  0
                         for (Notification notification: available_notifications) {
 285  0
                                 LOG.info("notification: " + notification);
 286  0
                                 notification.setLockedDate(new Timestamp(System.currentTimeMillis()));
 287  0
                                 businessObjectDao.save(notification);
 288  
                         }
 289  
                 }
 290  
 
 291  
 
 292  0
                 return available_notifications;
 293  
         }
 294  
 
 295  
         /**
 296  
          * Unlocks specified notification
 297  
          * @param notification the notification object to unlock
 298  
          */
 299  
         //switch to JPA criteria
 300  
         public void unlockNotification(Notification notification) {
 301  
 //                Map<String, Long> criteria = new HashMap<String, Long>();
 302  
 //                criteria.put(NotificationConstants.BO_PROPERTY_NAMES.ID, notification.getId());
 303  
 //                Criteria criteria = new Criteria();
 304  
 //                criteria.addEqualTo(NotificationConstants.BO_PROPERTY_NAMES.ID, notification.getId());
 305  
                 //criteria = Util.makeSelectForUpdate(criteria);
 306  
 
 307  
                 //                Criteria criteria = new Criteria(Notification.class.getName());
 308  
                 //                criteria.eq(NotificationConstants.BO_PROPERTY_NAMES.ID, notification.getId());
 309  
 
 310  
                 //Collection<Notification> notifications = businessObjectDao.findMatching(Notification.class, criteria, true, RiceConstants.NO_WAIT);
 311  
                 
 312  0
                 Collection<Notification> notifications = notDao.findMatchedNotificationsForUnlock(notification, businessObjectDao);
 313  
                 
 314  0
                 if (notifications == null || notifications.size() == 0) {
 315  0
                         throw new RuntimeException("Notification #" + notification.getId() + " not found to unlock");
 316  
                 }
 317  
 
 318  0
                 Notification n = notifications.iterator().next();
 319  0
                 n.setLockedDate(null);
 320  
 
 321  0
                 businessObjectDao.save(n);
 322  0
         }
 323  
 }